Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/buildbot/buildbot/status/tinderbox.py
diff options
context:
space:
mode:
Diffstat (limited to 'buildbot/buildbot/status/tinderbox.py')
-rw-r--r--buildbot/buildbot/status/tinderbox.py223
1 files changed, 223 insertions, 0 deletions
diff --git a/buildbot/buildbot/status/tinderbox.py b/buildbot/buildbot/status/tinderbox.py
new file mode 100644
index 0000000..51d404b
--- /dev/null
+++ b/buildbot/buildbot/status/tinderbox.py
@@ -0,0 +1,223 @@
+
+from email.Message import Message
+from email.Utils import formatdate
+
+from zope.interface import implements
+from twisted.internet import defer
+
+from buildbot import interfaces
+from buildbot.status import mail
+from buildbot.status.builder import SUCCESS, WARNINGS
+from buildbot.steps.shell import WithProperties
+
+import zlib, bz2, base64
+
+# TODO: docs, maybe a test of some sort just to make sure it actually imports
+# and can format email without raising an exception.
+
+class TinderboxMailNotifier(mail.MailNotifier):
+ """This is a Tinderbox status notifier. It can send e-mail to a number of
+ different tinderboxes or people. E-mails are sent at the beginning and
+ upon completion of each build. It can be configured to send out e-mails
+ for only certain builds.
+
+ The most basic usage is as follows::
+ TinderboxMailNotifier(fromaddr="buildbot@localhost",
+ tree="MyTinderboxTree",
+ extraRecipients=["tinderboxdaemon@host.org"])
+
+ The builder name (as specified in master.cfg) is used as the "build"
+ tinderbox option.
+
+ """
+ implements(interfaces.IEmailSender)
+
+ compare_attrs = ["extraRecipients", "fromaddr", "categories", "builders",
+ "addLogs", "relayhost", "subject", "binaryURL", "tree",
+ "logCompression", "errorparser", "columnName",
+ "useChangeTime"]
+
+ def __init__(self, fromaddr, tree, extraRecipients,
+ categories=None, builders=None, relayhost="localhost",
+ subject="buildbot %(result)s in %(builder)s", binaryURL="",
+ logCompression="", errorparser="unix", columnName=None,
+ useChangeTime=False):
+ """
+ @type fromaddr: string
+ @param fromaddr: the email address to be used in the 'From' header.
+
+ @type tree: string
+ @param tree: The Tinderbox tree to post to.
+
+ @type extraRecipients: tuple of string
+ @param extraRecipients: E-mail addresses of recipients. This should at
+ least include the tinderbox daemon.
+
+ @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 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 relayhost: string
+ @param relayhost: the host to which the outbound SMTP connection
+ should be made. Defaults to 'localhost'
+
+ @type subject: string
+ @param subject: a string to be used as the subject line of the message.
+ %(builder)s will be replaced with the name of the
+ %builder which provoked the message.
+ This parameter is not significant for the tinderbox
+ daemon.
+
+ @type binaryURL: string
+ @param binaryURL: If specified, this should be the location where final
+ binary for a build is located.
+ (ie. http://www.myproject.org/nightly/08-08-2006.tgz)
+ It will be posted to the Tinderbox.
+
+ @type logCompression: string
+ @param logCompression: The type of compression to use on the log.
+ Valid options are"bzip2" and "gzip". gzip is
+ only known to work on Python 2.4 and above.
+
+ @type errorparser: string
+ @param errorparser: The error parser that the Tinderbox server
+ should use when scanning the log file.
+ Default is "unix".
+
+ @type columnName: string
+ @param columnName: When columnName is None, use the buildername as
+ the Tinderbox column name. When columnName is a
+ string this exact string will be used for all
+ builders that this TinderboxMailNotifier cares
+ about (not recommended). When columnName is a
+ WithProperties instance it will be interpolated
+ as such. See WithProperties for more detail.
+ @type useChangeTime: bool
+ @param useChangeTime: When True, the time of the first Change for a
+ build is used as the builddate. When False,
+ the current time is used as the builddate.
+ """
+
+ mail.MailNotifier.__init__(self, fromaddr, categories=categories,
+ builders=builders, relayhost=relayhost,
+ subject=subject,
+ extraRecipients=extraRecipients,
+ sendToInterestedUsers=False)
+ self.tree = tree
+ self.binaryURL = binaryURL
+ self.logCompression = logCompression
+ self.errorparser = errorparser
+ self.useChangeTime = useChangeTime
+ assert columnName is None or type(columnName) is str \
+ or isinstance(columnName, WithProperties), \
+ "columnName must be None, a string, or a WithProperties instance"
+ self.columnName = columnName
+
+ def buildStarted(self, name, build):
+ builder = build.getBuilder()
+ if self.builders is not None and name not in self.builders:
+ return # ignore this Build
+ if self.categories is not None and \
+ builder.category not in self.categories:
+ return # ignore this build
+ self.buildMessage(name, build, "building")
+
+ def buildMessage(self, name, build, results):
+ text = ""
+ res = ""
+ # shortform
+ t = "tinderbox:"
+
+ text += "%s tree: %s\n" % (t, self.tree)
+ # the start time
+ # getTimes() returns a fractioned time that tinderbox doesn't understand
+ builddate = int(build.getTimes()[0])
+ # attempt to pull a Change time from this Build's Changes.
+ # if that doesn't work, fall back on the current time
+ if self.useChangeTime:
+ try:
+ builddate = build.getChanges()[-1].when
+ except:
+ pass
+ text += "%s builddate: %s\n" % (t, builddate)
+ text += "%s status: " % t
+
+ if results == "building":
+ res = "building"
+ text += res
+ elif results == SUCCESS:
+ res = "success"
+ text += res
+ elif results == WARNINGS:
+ res = "testfailed"
+ text += res
+ else:
+ res += "busted"
+ text += res
+
+ text += "\n";
+
+ if self.columnName is None:
+ # use the builder name
+ text += "%s build: %s\n" % (t, name)
+ elif type(self.columnName) is str:
+ # use the exact string given
+ text += "%s build: %s\n" % (t, self.columnName)
+ elif isinstance(self.columnName, WithProperties):
+ # interpolate the WithProperties instance, use that
+ text += "%s build: %s\n" % (t, build.getProperties().render(self.columnName))
+ else:
+ raise Exception("columnName is an unhandled value")
+ text += "%s errorparser: %s\n" % (t, self.errorparser)
+
+ # if the build just started...
+ if results == "building":
+ text += "%s END\n" % t
+ # if the build finished...
+ else:
+ text += "%s binaryurl: %s\n" % (t, self.binaryURL)
+ text += "%s logcompression: %s\n" % (t, self.logCompression)
+
+ # logs will always be appended
+ logEncoding = ""
+ tinderboxLogs = ""
+ for log in build.getLogs():
+ l = ""
+ if self.logCompression == "bzip2":
+ compressedLog = bz2.compress(log.getText())
+ l = base64.encodestring(compressedLog)
+ logEncoding = "base64";
+ elif self.logCompression == "gzip":
+ compressedLog = zlib.compress(log.getText())
+ l = base64.encodestring(compressedLog)
+ logEncoding = "base64";
+ else:
+ l = log.getText()
+ tinderboxLogs += l
+
+ text += "%s logencoding: %s\n" % (t, logEncoding)
+ text += "%s END\n\n" % t
+ text += tinderboxLogs
+ text += "\n"
+
+ m = Message()
+ m.set_payload(text)
+
+ m['Date'] = formatdate(localtime=True)
+ m['Subject'] = self.subject % { 'result': res,
+ 'builder': name,
+ }
+ m['From'] = self.fromaddr
+ # m['To'] is added later
+
+ d = defer.DeferredList([])
+ d.addCallback(self._gotRecipients, self.extraRecipients, m)
+ return d
+