[Buildbot-commits] buildbot/buildbot/status progress.py,1.10,1.11 client.py,1.15,1.16 html.py,1.54,1.55 words.py,1.33,1.34 builder.py,1.50,1.51 mail.py,1.14,1.15
Brian Warner
warner at users.sourceforge.net
Sun Apr 24 21:30:27 UTC 2005
- Previous message (by thread): [Buildbot-commits] buildbot/buildbot/process factory.py,1.8,1.9 step.py,1.61,1.62 base.py,1.47,1.48 interlock.py,1.6,1.7 step_twisted.py,1.67,1.68
- Next message (by thread): [Buildbot-commits] buildbot/buildbot/test test_changes.py,1.1,1.2 test_util.py,1.1,1.2 test_mailparse.py,1.1,1.2 test_config.py,1.14,1.15 test_steps.py,1.11,1.12 test_run.py,1.23,1.24 test_maildir.py,1.3,1.4 test_vc.py,1.24,1.25 test_web.py,1.8,1.9 test_status.py,1.13,1.14 test_interlock.py,1.1,1.2 test_slavecommand.py,1.10,1.11 test_twisted.py,1.3,1.4 test_control.py,1.3,1.4
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
Update of /cvsroot/buildbot/buildbot/buildbot/status
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29418/buildbot/status
Modified Files:
progress.py client.py html.py words.py builder.py mail.py
Log Message:
Revision: arch at buildbot.sf.net--2004/buildbot--dev--0--patch-83
Creator: Brian Warner <warner at monolith.lothar.com>
Merged from org.apestaart at thomas/buildbot--waterfall--0--patch-22
Merged builder-categories and waterfall CSS work from Thomas. Also added
test-case-name tags.
Patches applied:
* org.apestaart at thomas/buildbot--trial--0--base-0
tag of org.apestaart at thomas/buildbot--releases--0.6.2--patch-2
* org.apestaart at thomas/buildbot--trial--0--patch-1
adding test-case-name
* org.apestaart at thomas/buildbot--waterfall--0--base-0
tag of org.apestaart at thomas/buildbot--releases--0.6.2--patch-2
* org.apestaart at thomas/buildbot--waterfall--0--patch-1
* org.apestaart at thomas/buildbot--waterfall--0--patch-2
* org.apestaart at thomas/buildbot--waterfall--0--patch-3
* org.apestaart at thomas/buildbot--waterfall--0--patch-4
* org.apestaart at thomas/buildbot--waterfall--0--patch-5
* org.apestaart at thomas/buildbot--waterfall--0--patch-6
* org.apestaart at thomas/buildbot--waterfall--0--patch-7
* org.apestaart at thomas/buildbot--waterfall--0--patch-8
* org.apestaart at thomas/buildbot--waterfall--0--patch-9
merge for test-case-name
* org.apestaart at thomas/buildbot--waterfall--0--patch-10
unittests + fixes for status.mail category filtering
* org.apestaart at thomas/buildbot--waterfall--0--patch-11
fix testsuite by prefixing page title with BuildBot
* org.apestaart at thomas/buildbot--waterfall--0--patch-12
move category from Builder to BuilderStatus
* org.apestaart at thomas/buildbot--waterfall--0--patch-13
fix silly bug, makes order in waterfall work again
* org.apestaart at thomas/buildbot--waterfall--0--patch-14
document category and categories for builders and statusclients
* org.apestaart at thomas/buildbot--waterfall--0--patch-15
remove prints from test_run
* org.apestaart at thomas/buildbot--waterfall--0--patch-16
remove FIXME and unneeded code for category
* org.apestaart at thomas/buildbot--waterfall--0--patch-17
put back "builders" argument
* org.apestaart at thomas/buildbot--waterfall--0--patch-18
use class_ to assign a class="" to the html blocks
* org.apestaart at thomas/buildbot--waterfall--0--patch-19
cssclass->class_
* org.apestaart at thomas/buildbot--waterfall--0--patch-20
give classes names as agreed
* org.apestaart at thomas/buildbot--waterfall--0--patch-21
finish class styling and add EXCEPTION result
* org.apestaart at thomas/buildbot--waterfall--0--patch-22
classic buildbot stylesheet
Index: builder.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/builder.py,v
retrieving revision 1.50
retrieving revision 1.51
diff -u -d -r1.50 -r1.51
--- builder.py 23 Apr 2005 20:07:47 -0000 1.50
+++ builder.py 24 Apr 2005 21:30:25 -0000 1.51
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+# -*- test-case-name: buildbot.test.test_status -*-
from __future__ import generators
@@ -14,8 +14,8 @@
# sibling imports
from buildbot import interfaces, util
-SUCCESS, WARNINGS, FAILURE, SKIPPED = range(4)
-Results = ["success", "warnings", "failure", "skipped"]
+SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION = range(5)
+Results = ["success", "warnings", "failure", "skipped", "exception"]
# build processes call the following methods:
@@ -378,7 +378,7 @@
return self.progress.remaining()
# Once you know the step has finished, the following methods are legal.
- # Before ths step has finished, they all return None.
+ # Before this step has finished, they all return None.
def getText(self):
"""Returns a list of strings which describe the step. These are
@@ -389,8 +389,8 @@
def getColor(self):
"""Returns a single string with the color that should be used to
- display this step. 'green', 'orange', 'red' and 'yellow' are the
- most likely ones."""
+ display this step. 'green', 'orange', 'red', 'yellow' and 'purple'
+ are the most likely ones."""
return self.color
def getResults(self):
@@ -848,6 +848,10 @@
I live in the buildbot.process.base.Builder object, in the .statusbag
attribute.
+
+ @type category: string
+ @ivar category: user-defined category this builder belongs to; can be
+ used to filter on in status clients
"""
__implements__ = interfaces.IBuilderStatus,
@@ -858,14 +862,16 @@
stepHorizon = 50 # prune steps in builds beyond this
logHorizon = 20 # prune logs in builds beyond this
slavename = None
+ category = None
currentBuild = None
currentBigState = "offline" # or idle/waiting/interlocked/building
ETA = None
nextBuildNumber = 0
basedir = None # filled in by our parent
- def __init__(self, buildername):
+ def __init__(self, buildername, category=None):
self.name = buildername
+ self.category = category
self.events = []
# these three hold Events, and are used to retrieve the current
@@ -1082,7 +1088,6 @@
eventIndex -= 1
e = self.getEvent(eventIndex)
-
def subscribe(self, receiver):
# will get builderChangedState, buildStarted, and buildFinished
self.watchers.append(receiver)
@@ -1413,8 +1418,18 @@
def getBuildbotURL(self):
return self.botmaster.parent.buildbotURL
- def getBuilderNames(self):
- return self.botmaster.builderNames[:] # don't let them break it
+ def getBuilderNames(self, categories=None):
+ if categories == None:
+ return self.botmaster.builderNames[:] # don't let them break it
+
+ l = []
+ # respect addition order
+ for name in self.botmaster.builderNames:
+ builder = self.botmaster.builders[name]
+ if builder.builder_status.category in categories:
+ l.append(name)
+ return l
+
def getBuilder(self, name):
"""
@rtype: L{BuilderStatus}
@@ -1436,7 +1451,7 @@
if t:
builder_status.subscribe(t)
- def builderAdded(self, name, basedir):
+ def builderAdded(self, name, basedir, category=None):
"""
@rtype: L{BuilderStatus}
"""
@@ -1450,8 +1465,12 @@
except:
log.msg("error while loading status pickle, creating a new one")
if not builder_status:
- builder_status = BuilderStatus(name)
+ builder_status = BuilderStatus(name, category)
builder_status.addPointEvent(["builder", "created"])
+ log.msg("added builder %s in category %s" % (name, category))
+ # an unpickled object might not have category set from before,
+ # so set it here to make sure
+ builder_status.category = category
builder_status.basedir = os.path.join(self.basedir, basedir)
builder_status.status = self
Index: client.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/client.py,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -d -r1.15 -r1.16
--- client.py 23 Apr 2005 10:37:00 -0000 1.15
+++ client.py 24 Apr 2005 21:30:25 -0000 1.16
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+# -*- test-case-name: buildbot.test.test_status -*-
from twisted.spread import pb
from twisted.python import log, components
Index: html.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/html.py,v
retrieving revision 1.54
retrieving revision 1.55
diff -u -d -r1.54 -r1.55
--- html.py 20 Apr 2005 20:13:25 -0000 1.54
+++ html.py 24 Apr 2005 21:30:25 -0000 1.55
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+# -*- test-case-name: buildbot.test.test_web -*-
from __future__ import generators
@@ -69,8 +69,11 @@
if comment:
data += "<!-- %s -->" % comment
data += "<td"
+ class_ = props.get('class_', None)
+ if class_:
+ props["class"] = class_
for prop in ("align", "bgcolor", "colspan", "rowspan", "border",
- "valign", "halign"):
+ "valign", "halign", "class"):
p = props.get(prop, None)
if p != None:
data += " %s=\"%s\"" % (prop, p)
@@ -82,14 +85,39 @@
data += "</td>\n"
return data
+def build_get_class(b):
+ """
+ Return the class to use for a finished build or buildstep,
+ based on the result.
+ """
+ # FIXME: this getResults duplicity might need to be fixed
+ result = b.getResults()
+ #print "THOMAS: result for b %r: %r" % (b, result)
+ if isinstance(b, builder.BuildStatus):
+ result = b.getResults()
+ elif isinstance(b, builder.BuildStepStatus):
+ result = b.getResults()[0]
+ # after forcing a build, b.getResults() returns ((None, []), []), ugh
+ if isinstance(result, tuple):
+ result = result[0]
+ else:
+ raise TypeError, "%r is not a BuildStatus or BuildStepStatus" % b
+
+ if result == None:
+ # FIXME: this happens when a buildstep is running ?
+ return "running"
+ return builder.Results[result]
+
class Box:
# a Box wraps an Event. The Box has HTML <td> parameters that Events
# lack, and it has a base URL to which each File's name is relative.
# Events don't know about HTML.
spacer = False
- def __init__(self, text=[], color=None, urlbase=None, **parms):
+ def __init__(self, text=[], color=None, class_=None, urlbase=None,
+ **parms):
self.text = text
self.color = color
+ self.class_ = class_
self.urlbase = urlbase
self.show_idle = 0
if parms.has_key('show_idle'):
@@ -105,10 +133,11 @@
text = self.text
if not text and self.show_idle:
text = ["[idle]"]
- return td(text, props, bgcolor=self.color)
+ return td(text, props, bgcolor=self.color, class_=self.class_)
class HtmlResource(Resource):
+ css = None
contentType = "text/html"
def render(self, request):
data = self.content(request)
@@ -120,6 +149,10 @@
title = "Dummy"
def content(self, request):
data = "<html>\n<head><title>" + self.title + "</title></head>\n"
+ if self.css:
+ data += ("<link href=\"%s\""
+ " rel=\"stylesheet\""
+ " type=\"text/css\">\n" % self.css)
data += "<body vlink=\"#800080\">\n"
data += self.body(request)
data += "</body></html>\n"
@@ -129,6 +162,7 @@
class StaticHTML(HtmlResource):
def __init__(self, body, title):
+ HtmlResource.__init__(self)
self.bodyHTML = body
self.title = title
def body(self, request):
@@ -666,20 +700,21 @@
else:
text.extend(["ETA: ?"])
- return Box(text, color)
+ return Box(text, color=color, class_="Activity " + state)
components.registerAdapter(CurrentBox, builder.BuilderStatus, ICurrentBox)
-class CommitBox(components.Adapter):
+class ChangeBox(components.Adapter):
__implements__ = IBox,
def getBox(self):
url = "changes/%d" % self.original.number
text = '<a href="%s">%s</a>' % (url, html.escape(self.original.who))
- return Box([text], "white")
-components.registerAdapter(CommitBox, changes.Change, IBox)
+ return Box([text], color="white", class_="Change")
+components.registerAdapter(ChangeBox, changes.Change, IBox)
class BuildBox(components.Adapter):
# this provides the yellow "starting line" box for each build
__implements__ = IBox,
+
def getBox(self):
b = self.original
name = b.getBuilder().getName()
@@ -687,12 +722,14 @@
url = "%s/builds/%d" % (name, number)
text = '<a href="%s">Build %d</a>' % (urllib.quote(url), number)
color = "yellow"
+ class_ = "start"
if b.isFinished() and not b.getSteps():
# the steps have been pruned, so there won't be any indication
# of whether it succeeded or failed. Color the box red or green
# to show its status
color = b.getColor()
- return Box([text], color)
+ class_ = build_get_class(b)
+ return Box([text], color=color, class_="BuildStep " + class_)
components.registerAdapter(BuildBox, builder.BuildStatus, IBox)
class StepBox(components.Adapter):
@@ -712,14 +749,20 @@
name = logs[num].getName()
url = urllib.quote("%s/%d" % (urlbase, num))
text.append("<a href=\"%s\">%s</a>" % (url, html.escape(name)))
- return Box(text, self.original.getColor())
+ color = self.original.getColor()
+ class_ = "BuildStep " + build_get_class(self.original)
+ return Box(text, color, class_=class_)
components.registerAdapter(StepBox, builder.BuildStepStatus, IBox)
class EventBox(components.Adapter):
__implements__ = IBox,
def getBox(self):
text = self.original.getText()
- return Box(text, self.original.getColor())
+ color = self.original.getColor()
+ class_ = "Event"
+ if color:
+ class_ += " " + color
+ return Box(text, color, class_=class_)
components.registerAdapter(EventBox, builder.Event, IBox)
@@ -731,14 +774,16 @@
assert interfaces.IBuilderStatus(self.original)
b = self.original.getLastFinishedBuild()
if not b:
- return Box(["none"], "white")
+ return Box(["none"], "white", class_="LastBuild")
name = b.getBuilder().getName()
number = b.getNumber()
url = "%s/builds/%d" % (name, number)
text = b.getText()
# TODO: add logs?
# TODO: add link to the per-build page at 'url'
- return Box(text, b.getColor())
+ c = b.getColor()
+ class_ = build_get_class(b)
+ return Box(text, c, class_="LastBuild %s" % class_)
components.registerAdapter(BuildTopBox, builder.BuilderStatus, ITopBox)
class Spacer(builder.Event):
@@ -796,10 +841,15 @@
"""This builds the main status page, with the waterfall display, and
all child pages."""
title = "BuildBot"
- def __init__(self, status, changemaster):
+ def __init__(self, status, changemaster, categories, css=None):
HtmlResource.__init__(self)
self.status = status
self.changemaster = changemaster
+ self.categories = categories
+ p = self.status.getProjectName()
+ if p:
+ self.title = "BuildBot: %s" % p
+ self.css = css
def body(self, request):
"This method builds the main waterfall display."
@@ -807,25 +857,29 @@
phase = int(phase[0])
showBuilders = request.args.get("show", None)
+ allBuilders = self.status.getBuilderNames(categories=self.categories)
if showBuilders:
builderNames = []
for b in showBuilders:
- if b in self.status.getBuilderNames() and \
- not b in builderNames:
- builderNames.append(b)
+ if b not in allBuilders:
+ continue
+ if b in builderNames:
+ continue
+ builderNames.append(b)
else:
- builderNames = self.status.getBuilderNames()
+ builderNames = allBuilders
builders = map(lambda name: self.status.getBuilder(name),
builderNames)
if phase == -1:
return self.body0(request, builders)
- (sourceNames, timestamps, eventGrid, sourceEvents) = \
+ (changeNames, builderNames, timestamps, eventGrid, sourceEvents) = \
self.buildGrid(request, builders)
if phase == 0:
return self.phase0(request, sourceNames, timestamps, eventGrid)
# start the table: top-header material
- data = "<table frame=\"rhs\" rules=\"all\">\n"
+ data = "<table class=\"table\" border=\"0\" cellspacing=\"0\">\n"
+ #data = "<table frame=\"rhs\" rules=\"all\" class=\"table\">\n"
data += " <tr>\n"
projectName = self.status.getProjectName()
@@ -836,33 +890,39 @@
(projectURL, projectName)
else:
topleft = "last build"
- data += td(topleft, align="right", colspan=2)
+ data += td(topleft, align="right", colspan=2, class_="Project")
for b in builders:
box = ITopBox(b).getBox()
data += box.td(align="center")
data += " </tr>\n"
data += " <tr>\n"
- data += td("current activity", align="right", colspan=2)
+ data += td("current activity", align="right", colspan=2,
+ class_="Activity")
for b in builders:
box = ICurrentBox(b).getBox()
data += box.td(align="center")
data += " </tr>\n"
data += " <tr>\n"
- data += td("time", align="center")
- for name in sourceNames:
+ data += td("time", align="center", class_="Time")
+ name = changeNames[0]
+ data += td(
+ "<a href=\"%s\">%s</a>" % (urllib.quote(name), name),
+ align="center", class_="Change")
+ for name in builderNames:
data += td(
#"<a href=\"%s\">%s</a>" % (request.childLink(name), name),
"<a href=\"%s\">%s</a>" % (urllib.quote(name), name),
- align="center")
+ align="center", class_="Builder")
data += " </tr>\n"
if phase == 1:
f = self.phase1
else:
f = self.phase2
- data += f(request, sourceNames, timestamps, eventGrid, sourceEvents)
+ data += f(request, changeNames + builderNames, timestamps, eventGrid,
+ sourceEvents)
data += "</table>\n"
@@ -894,7 +954,8 @@
data += " for the waterfall display</p>\n"
#data += "<table border=\"1\">\n"
- data += "<table frame=\"rhs\" rules=\"all\">\n"
+ #data += "<table frame=\"rhs\" rules=\"all\" class=\"table\">\n"
+ data += "<table class=\"table\" border=\"0\" cellspacing=\"0\">\n"
names = map(lambda builder: builder.name, builders)
# the top row is two blank spaces, then the top-level status boxes
@@ -945,8 +1006,9 @@
lastEventTime = util.now()
sources = [commit_source] + builders
- sourceNames = ["changes"] + map(lambda builder: builder.getName(),
- builders)
+ changeNames = ["changes"]
+ builderNames = map(lambda builder: builder.getName(), builders)
+ sourceNames = changeNames + builderNames
sourceEvents = []
sourceGenerators = []
for s in sources:
@@ -1046,7 +1108,7 @@
# loop is finished. now we have eventGrid[] and timestamps[]
if debugGather: log.msg("finished loop")
assert(len(timestamps) == len(eventGrid))
- return (sourceNames, timestamps, eventGrid, sourceEvents)
+ return (changeNames, builderNames, timestamps, eventGrid, sourceEvents)
def phase0(self, request, sourceNames, timestamps, eventGrid):
# phase0 rendering
@@ -1103,7 +1165,7 @@
time.strftime("%H:%M:%S",
time.localtime(timestamps[r])))
data += td(stuff, valign="bottom", align="center",
- rowspan=maxRows)
+ rowspan=maxRows, class_="Time")
for c in range(0, len(chunkstrip)):
block = chunkstrip[c]
assert(block != None) # should be [] instead
@@ -1160,8 +1222,9 @@
stuff.append(
time.strftime("%H:%M:%S",
time.localtime(timestamps[r])))
- grid[0].append(Box(text=stuff,
+ grid[0].append(Box(text=stuff, class_="Time",
valign="bottom", align="center"))
+
# at this point the timestamp column has been populated with
# maxRows boxes, most None but the last one has the time string
for c in range(0, len(chunkstrip)):
@@ -1264,7 +1327,7 @@
control = None
favicon = None
- def __init__(self, status, control, changemaster):
+ def __init__(self, status, control, changemaster, categories, css):
"""
@type status: L{buildbot.status.builder.Status}
@type control: L{buildbot.master.Control}
@@ -1274,7 +1337,10 @@
self.status = status
self.control = control
self.changemaster = changemaster
- waterfall = WaterfallStatusResource(self.status, changemaster)
+ self.categories = categories
+ self.css = css
+ waterfall = WaterfallStatusResource(self.status, changemaster,
+ categories, css)
self.putChild("", waterfall)
def render(self, request):
@@ -1282,18 +1348,22 @@
request.finish()
def getChild(self, path, request):
- if path in self.status.getBuilderNames():
- builder = self.status.getBuilder(path)
- control = None
- if self.control:
- control = self.control.getBuilder(path)
- return StatusResourceBuilder(builder, control)
+ if path == "buildbot.css" and self.css:
+ return static.File("buildbot.css")
if path == "changes":
return StatusResourceChanges(self.changemaster)
if path == "favicon.ico":
if self.favicon:
return static.File(self.favicon)
return NoResource("No favicon.ico registered")
+
+ if path in self.status.getBuilderNames():
+ builder = self.status.getBuilder(path)
+ control = None
+ if self.control:
+ control = self.control.getBuilder(path)
+ return StatusResourceBuilder(builder, control)
+
return NoResource("No such Builder '%s'" % path)
# the icon is sibpath(__file__, "../buildbot.png") . This is for portability.
@@ -1313,6 +1383,25 @@
distributed web server (which lets the buildbot pages be a subset of some
other web server).
+ Since 0.6.3, BuildBot defines class attributes on elements so they can be
+ styled with CSS stylesheets. Buildbot uses some generic classes to
+ identify the type of object, and some more specific classes for the
+ various kinds of those types. It does this by specifying both in the
+ class attributes where applicable, separated by a space. It is important
+ that in your CSS you declare the more generic class styles above the more
+ specific ones. For example, first define a style for .Event, and below
+ that for .SUCCESS
+
+ The following CSS class names are used:
+ - Activity, Event, BuildStep, LastBuild: general classes
+ - waiting, interlocked, building, offline, idle: Activity states
+ - start, running, success, failure, warnings, skipped, exception:
+ LastBuild and BuildStep states
+ - Change: box with change
+ - Builder: box for builder name (at top)
+ - Project
+ - Time
+
@type parent: L{buildbot.master.BuildMaster}
@ivar parent: like all status plugins, this object is a child of the
BuildMaster, so C{.parent} points to a
@@ -1321,10 +1410,11 @@
"""
__implements__ = (interfaces.IStatusReceiver,
service.MultiService.__implements__)
- compare_attrs = ["http_port", "distrib_port", "allowForce"]
+ compare_attrs = ["http_port", "distrib_port", "allowForce",
+ "categories", "css"]
def __init__(self, http_port=None, distrib_port=None, allowForce=True,
- favicon=buildbot_icon):
+ categories=None, css=None, favicon=buildbot_icon):
"""
xxxTo have the buildbot run its own web server, pass a port number to
@@ -1369,6 +1459,8 @@
self.http_port = http_port
self.distrib_port = distrib_port
self.allowForce = allowForce
+ self.categories = categories
+ self.css = css
self.favicon = favicon
def __repr__(self):
@@ -1393,7 +1485,8 @@
else:
control = None
change_svc = self.parent.change_svc
- sr = StatusResource(status, control, change_svc)
+ sr = StatusResource(status, control, change_svc, self.categories,
+ self.css)
sr.favicon = self.favicon
self.site = server.Site(sr)
Index: mail.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/mail.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- mail.py 19 Apr 2005 07:45:19 -0000 1.14
+++ mail.py 24 Apr 2005 21:30:25 -0000 1.15
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+# -*- test-case-name: buildbot.test.test_status -*-
# the email.MIMEMultipart module is only available in python-2.2.2 and later
@@ -17,7 +17,7 @@
from twisted.python import components, log
from buildbot import interfaces, util
-from buildbot.status import builder
+from buildbot.status.builder import FAILURE, SUCCESS, WARNINGS
class Domain(util.ComparableMixin):
@@ -56,10 +56,10 @@
service.Service.__implements__)
compare_attrs = ["extraRecipients", "lookup", "fromaddr", "mode",
- "builders", "addLogs", "relayhost", "subject",
- "sendToInterestedUsers"]
+ "categories", "builders", "addLogs", "relayhost",
+ "subject", "sendToInterestedUsers"]
- def __init__(self, fromaddr, mode="all", builders=None,
+ def __init__(self, fromaddr, mode="all", categories=None, builders=None,
addLogs=False, relayhost="localhost",
subject="buildbot %(result)s in %(builder)s",
lookup=None, extraRecipients=[],
@@ -93,9 +93,16 @@
- 'problem': only send mail about a build which failed
when the previous build passed
- @type builders: tuple of strings
- @param builders: a list of builder names for which mail should be sent.
- Defaults to all builds.
+ @type builders: list of strings
+ @param builders: a list of builder names for which mail should be
+ sent. Defaults to None (send mail for all builds).
+ Use either builders or categories, but not both.
+
+ @type categories: list of strings
+ @param categories: a list of category names to serve status
+ information for. Defaults to None (all
+ categories). Use either builders or categories,
+ but not both.
@type addLogs: boolean.
@param addLogs: if True, include all build logs as attachments to the
@@ -129,6 +136,7 @@
self.sendToInterestedUsers = sendToInterestedUsers
self.fromaddr = fromaddr
self.mode = mode
+ self.categories = categories
self.builders = builders
self.addLogs = addLogs
self.relayhost = relayhost
@@ -141,6 +149,11 @@
self.watched = []
self.status = None
+ # you should either limit on builders or categories, not both
+ if self.builders != None and self.categories != None:
+ log.err("Please specify only builders to ignore or categories to include")
+ raise # FIXME: the asserts above do not raise some Exception either
+
def setServiceParent(self, parent):
"""
@type parent: L{buildbot.master.BuildMaster}
@@ -159,8 +172,13 @@
return service.Service.disownServiceParent(self)
def builderAdded(self, name, builder):
+ # only subscribe to builders we are interested in
+ if self.categories != None and builder.category not in self.categories:
+ return None
+
self.watched.append(builder)
- return self # subscribe to all builders
+ return self # subscribe to this builder
+
def builderRemoved(self, name):
pass
@@ -170,15 +188,20 @@
pass
def buildFinished(self, name, build, results):
# here is where we actually do something.
- if self.builders != None and name in self.builders:
+ builder = build.getBuilder()
+ if self.builders is not None and name not in self.builders:
return # ignore this build
- if self.mode == "failing" and results != builder.FAILURE:
+ if self.categories is not None and \
+ builder.category not in self.categories:
+ return # ignore this build
+
+ if self.mode == "failing" and results != FAILURE:
return
if self.mode == "problem":
- if results != builder.FAILURE:
+ if results != FAILURE:
return
prev = build.getPreviousBuild()
- if prev and prev.getResults() == builder.FAILURE:
+ if prev and prev.getResults() == FAILURE:
return
# for testing purposes, buildMessage returns a Deferred that fires
# when the mail has been sent. To help unit tests, we return that
@@ -227,10 +250,10 @@
else:
t = ""
- if results == builder.SUCCESS:
+ if results == SUCCESS:
text += "Build succeeded!\n"
res = "success"
- elif results == builder.WARNINGS:
+ elif results == WARNINGS:
text += "Build Had Warnings%s\n" % t
res = "warnings"
else:
Index: progress.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/progress.py,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- progress.py 23 Sep 2004 20:51:33 -0000 1.10
+++ progress.py 24 Apr 2005 21:30:25 -0000 1.11
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+# -*- test-case-name: buildbot.test.test_status -*-
from twisted.internet import reactor
from twisted.spread import pb
Index: words.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/words.py,v
retrieving revision 1.33
retrieving revision 1.34
diff -u -d -r1.33 -r1.34
--- words.py 19 Apr 2005 07:45:19 -0000 1.33
+++ words.py 24 Apr 2005 21:30:25 -0000 1.34
@@ -27,7 +27,7 @@
"What you say !!": ["You have no chance to survive make your time.",
"HA HA HA HA ...."],
}
- def __init__(self, nickname, channels, status):
+ def __init__(self, nickname, channels, status, categories):
"""
@type nickname: string
@param nickname: the nickname by which this bot should be known
@@ -40,6 +40,7 @@
self.nickname = nickname
self.channels = channels
self.status = status
+ self.categories = categories
self.counter = 0
self.hasQuit = 0
@@ -131,7 +132,7 @@
"""
@rtype: list of L{buildbot.process.builder.Builder}
"""
- names = self.status.getBuilderNames()
+ names = self.status.getBuilderNames(categories=self.categories)
names.sort()
builders = [self.status.getBuilder(n) for n in names]
return builders
@@ -171,7 +172,11 @@
str = "Configured builders: "
for b in builders:
str += b.name
- if not b.remote:
+ # FIXME: b is a buildbot.status.builder.BuilderStatus
+ # has no .remote, so maybe it should be added there
+ #if not b.remote:
+ state = b.getState()[0]
+ if state == 'offline':
str += "[offline]"
str += " "
str.rstrip()
@@ -222,6 +227,12 @@
WARNINGS: "Warnings",
FAILURE: "Failure",
}
+
+ # only notify about builders we are interested in
+ log.msg('builder %r in category %s finished' % (b, b.category))
+ if self.categories != None and b.category not in self.categories:
+ return
+
r = "Hey! build %s #%d is complete: %s" % \
(b.getBuilder().getName(),
b.getNumber(),
@@ -431,11 +442,12 @@
shuttingDown = False
p = None
- def __init__(self, nickname, channels):
+ def __init__(self, nickname, channels, categories):
#ThrottledClientFactory.__init__(self) # doesn't exist
self.status = None
self.nickname = nickname
self.channels = channels
+ self.categories = categories
def __getstate__(self):
d = self.__dict__.copy()
@@ -448,7 +460,8 @@
self.p.quit("buildmaster reconfigured: bot disconnecting")
def buildProtocol(self, address):
- p = self.protocol(self.nickname, self.channels, self.status)
+ p = self.protocol(self.nickname, self.channels, self.status,
+ self.categories)
p.factory = self
p.status = self.status
p.control = self.control
@@ -476,11 +489,13 @@
connect to a single IRC server and am known by a single nickname on that
server, however I can join multiple channels."""
- compare_attrs = ["host", "port", "nick", "channels", "allowForce"]
+ compare_attrs = ["host", "port", "nick", "channels", "allowForce",
+ "categories"]
__implements__ = (interfaces.IStatusReceiver,
service.MultiService.__implements__)
- def __init__(self, host, nick, channels, port=6667, allowForce=True):
+ def __init__(self, host, nick, channels, port=6667, allowForce=True,
+ categories=None):
service.MultiService.__init__(self)
assert allowForce in (True, False) # TODO: implement others
@@ -491,9 +506,10 @@
self.nick = nick
self.channels = channels
self.allowForce = allowForce
+ self.categories = categories
# need to stash the factory so we can give it the status object
- self.f = IrcStatusFactory(self.nick, self.channels)
+ self.f = IrcStatusFactory(self.nick, self.channels, self.categories)
c = internet.TCPClient(host, port, self.f)
c.setServiceParent(self)
- Previous message (by thread): [Buildbot-commits] buildbot/buildbot/process factory.py,1.8,1.9 step.py,1.61,1.62 base.py,1.47,1.48 interlock.py,1.6,1.7 step_twisted.py,1.67,1.68
- Next message (by thread): [Buildbot-commits] buildbot/buildbot/test test_changes.py,1.1,1.2 test_util.py,1.1,1.2 test_mailparse.py,1.1,1.2 test_config.py,1.14,1.15 test_steps.py,1.11,1.12 test_run.py,1.23,1.24 test_maildir.py,1.3,1.4 test_vc.py,1.24,1.25 test_web.py,1.8,1.9 test_status.py,1.13,1.14 test_interlock.py,1.1,1.2 test_slavecommand.py,1.10,1.11 test_twisted.py,1.3,1.4 test_control.py,1.3,1.4
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the Commits
mailing list