Extend the tinderbox mail notifier with the ability to report the locale-specific parts of a log to separate trees.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Tue, 09 Sep 2008 10:26:20 -0400
changeset 9 68add6686cd8def893caf2c5eef067b5e9fe4e33
parent 0 b91b4647ed5b73266abe0dde894e694c6a97f303
child 10 90a87727bf24ef96809a6c4cf920576001a19af4
push id4
push userbsmedberg@mozilla.com
push dateTue, 09 Sep 2008 15:34:27 +0000
Extend the tinderbox mail notifier with the ability to report the locale-specific parts of a log to separate trees.
status/__init__.py
status/tinderbox.py
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/status/tinderbox.py
@@ -0,0 +1,190 @@
+import buildbot
+from buildbot.status.tinderbox import TinderboxMailNotifier
+
+try:
+    # The buildbot 0.7.7 version
+    from buildbot.process.buildstep import render_properties
+
+    class LocaleProperties(object):
+        def __init__(self, build, locale):
+            self.build = build
+            self.locale = locale
+
+        def getProperty(self, name):
+            if name == 'locale':
+                return self.locale
+            return self.build.getProperty(name)
+
+except ImportError:
+    # The buildbot 0.7.8 version
+    from buildbot.process.properties import Properties
+
+    def render_properties(s, build):
+        return build.getProperties().render(s)
+
+    class LocaleProperties():
+        def __init__(self, build, locale):
+            self.properties = Properties()
+            self.properties.updateFromProperties(build.getProperties())
+            self.properties.setProperty('locale', locale, 'build step')
+
+        def getProperties(self):
+            return self.properties
+
+from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION
+
+from email.Message import Message
+from email.Utils import formatdate
+
+from twisted.internet import defer
+
+from zope.interface import implements
+
+from cStringIO import StringIO
+
+import zlib, bz2, base64
+
+def iterLocaleResults(buildresults, locale):
+    """Given a buildresults object, iterate over the step results for
+    a particular locale."""
+
+    for stepresult in buildresults.getSteps():
+        if hasattr(stepresult, 'locale') and stepresult.locale == locale:
+            yield stepresult
+
+def mergeResults(r1, r2):
+    """Find the worse of two results."""
+    if r1 == EXCEPTION or r2 == EXCEPTION:
+        return EXCEPTION
+    if r1 == FAILURE or r2 == FAILURE:
+        return FAILURE
+    if r1 == WARNINGS or r2 == WARNINGS:
+        return WARNINGS
+    if r1 == SUCCESS or r2 == SUCCESS:
+        return SUCCESS
+    return SKIPPED
+
+def printBuildResult(br, fd):
+    """Take a BuildResult and turn it into a tinderbox-appropriate log."""
+    shortText = (' '.join(br.getText()) + '\n').replace('TinderboxPrint',
+                                                        'Tinderbox Print')
+
+    print >>fd, "======== started ========"
+    print >>fd, shortText,
+    print >>fd, "=== Output ==="
+    for log in br.getLogs():
+        logText = log.getTextWithHeaders()
+        for line in logText.splitlines():
+            if not line.startswith('TinderboxPrint'):
+                line = line.replace('TinderboxPrint', 'Tinderbox Print')
+            print >>fd, line
+    print >>fd, '=== Output end ==='
+    print >>fd, '======== BuildStep end ========'
+
+class TinderboxLocaleMailNotifier(TinderboxMailNotifier):
+    """This is a Tinderbox status notifier for localized builds. The main
+    build log contains all of the locales, and in addition each localized
+    build sends a separate log notification with the general step results
+    and the results peculiar to that particular locale."""
+
+    implements(buildbot.interfaces.IEmailSender)
+
+    compare_attrs = list(TinderboxMailNotifier.compare_attrs) + ['localeTree', 'localeColumnName']
+
+    def __init__(self, localeColumnName, localeTree=None,
+                 localeBinaryURL='', **kwargs):
+        """
+        @param localeColumnName: string or WithProperties specifying the
+                                 column name for reporting localized builds
+        @param localeTree:       string or WithProperties specifying the
+                                 tree name for reporting localized builds. If
+                                 this parameter is None, it is the same as
+                                 the main tree name
+        @param localeBinaryURL:  string or WithProperties specifying the
+                                 download location of localized packages
+        @see TinderboxMailNotifier.__init__
+        """
+        TinderboxMailNotifier.__init__(self, **kwargs)
+        if localeTree is None:
+            localeTree = self.tree
+        self.localeTree = localeTree
+        self.localeColumnName = localeColumnName
+        self.localeBinaryURL = localeBinaryURL
+
+    def buildMessage(self, name, buildstatus, result):
+        # first send the 'main' message
+        TinderboxMailNotifier.buildMessage(self, name, buildstatus, result)
+
+        # now loop through locales and build a message for each
+        for locale in buildstatus.getProperty('locales'):
+            self.buildLocaleMessage(name, buildstatus, locale, result)
+
+    def buildLocaleMessage(self, name, buildstatus, locale, result):
+        """note: the results parameter here is the complete results of the
+        entire build... failures or warnings propagate to all locales, but
+        particular locales can fail even if the entire build succeeds."""
+
+        mailBody = StringIO()
+
+        localeBuild = LocaleProperties(buildstatus, locale)
+
+        if result == "building":
+            rvtext = "building"
+        else:
+            localeresult = result
+            for localebuildresult in iterLocaleResults(buildstatus, locale):
+                localeresult = mergeResults(localeresult,
+                                            localebuildresult.getResults()[0])
+            if localeresult == SUCCESS:
+                rvtext = "success"
+            elif localeresult == WARNINGS:
+                rvtext = "testfailed"
+            else:
+                rvtext = "busted"
+
+        print >>mailBody, "tinderbox: tree: %s" % render_properties(self.localeTree, localeBuild)
+        print >>mailBody, "tinderbox: builddate: %s" % int(buildstatus.getTimes()[0])
+        print >>mailBody, "tinderbox: status: %s" % rvtext
+        print >>mailBody, "tinderbox: build: %s" % render_properties(self.localeColumnName, localeBuild)
+        print >>mailBody, "tinderbox: errorparser: %s" % self.errorparser
+        if result == "building":
+            print >>mailBody, "tinderbox: END"
+        else:
+            logBody = StringIO()
+            # non-locale steps should be sent to each localized log
+            for br in buildstatus.getSteps():
+                if not br.isFinished(): continue
+                if hasattr(br, 'locale'): continue
+                printBuildResult(br, logBody)
+                    
+            for br in iterLocaleResults(buildstatus, locale):
+                if not br.isFinished(): continue
+                printBuildResult(br, logBody)
+
+            if self.logCompression == 'bzip2':
+                logtext = base64.encodestring(bz2.compress(logBody.getvalue()))
+                logencoding = 'base64'
+            elif self.logCompression == 'gzip':
+                logtext = base64.encodestring(zlib.compress(logBody.getvalue()))
+                logencoding = 'base64'
+            else:
+                logtext = logBody.getvalue()
+                logencoding = ''
+
+            print >>mailBody, "tinderbox: binaryurl: %s" % render_properties(self.localeBinaryURL, localeBuild)
+            print >>mailBody, "tinderbox: logcompression: %s" % self.logCompression
+            print >>mailBody, "tinderbox: logencoding: %s" % logencoding
+            print >>mailBody, "tinderbox: END"
+            print >>mailBody
+            print >>mailBody, logtext
+
+        m = Message()
+        m.set_payload(mailBody.getvalue())
+        m['Date'] = formatdate(localtime=True)
+        m['Subject'] = self.subject % { 'result': rvtext,
+                                        'builder': name }
+        m['From'] = self.fromaddr
+
+        d = defer.DeferredList([])
+        d.addCallback(self._gotRecipients, self.extraRecipients, m)
+        return d