diff options
Diffstat (limited to 'tools/buildbot/master/SVNMailNotifier.py')
-rw-r--r-- | tools/buildbot/master/SVNMailNotifier.py | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/tools/buildbot/master/SVNMailNotifier.py b/tools/buildbot/master/SVNMailNotifier.py new file mode 100644 index 0000000..1dfe839 --- /dev/null +++ b/tools/buildbot/master/SVNMailNotifier.py @@ -0,0 +1,210 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# +import os +import urllib +import re + +from email.Message import Message +from email.Utils import formatdate +from email.MIMEText import MIMEText + +from twisted.internet import defer +from twisted.application import service + +from buildbot.status.builder import FAILURE, SUCCESS, WARNINGS +from buildbot.status.mail import MailNotifier + +class SVNMailNotifier(MailNotifier): + """Implement custom status mails for the Subversion project""" + + def __init__(self, fromaddr, mode="all", categories=None, builders=None, + addLogs=False, relayhost="localhost", + subject="buildbot %(result)s in %(builder)s", + lookup=None, extraRecipients=[], + sendToInterestedUsers=True, + body="", + replytoaddr=""): + """ + @type body: string + @param body: a string to be used as the body of the message. + + @type replytoaddr: string + @param replytoaddr: the email address to be used in the 'Reply-To' header. + """ + + self.body = body + self.replytoaddr = replytoaddr + + # pass the rest of the parameters to our parent. + MailNotifier.__init__(self, fromaddr, mode, categories, builders, + addLogs, relayhost, subject, lookup, extraRecipients, + sendToInterestedUsers) + + def buildMessage(self, name, build, results): + if self.mode == "all": + intro = "The Buildbot has finished a build of %s.\n" % name + elif self.mode == "failing": + intro = "The Buildbot has detected a failed build of %s.\n" % name + else: + intro = "The Buildbot has detected a new failure of %s.\n" % name + + # buildurl + buildurl = self.status.getURLForThing(build) +# lgo: url's are already quoted now. +# if buildurl: +# buildurl = urllib.quote(buildurl, '/:') + + # buildboturl + buildboturl = self.status.getBuildbotURL() +# if url: +# buildboturl = urllib.quote(url, '/:') + + # reason of build + buildreason = build.getReason() + + # source stamp + patch = None + ss = build.getSourceStamp() + if ss is None: + source = "unavailable" + else: + if build.getChanges(): + revision = max([int(c.revision) for c in build.getChanges()]) + + source = "" + if ss.branch is None: + ss.branch = "trunk" + source += "[branch %s] " % ss.branch + if revision: + source += str(revision) + else: + source += "HEAD" + if ss.patch is not None: + source += " (plus patch)" + + # actual buildslave + buildslave = build.getSlavename() + + # TODO: maybe display changes here? or in an attachment? + + # status + t = build.getText() + if t: + t = ": " + " ".join(t) + else: + t = "" + + if results == SUCCESS: + status = "Build succeeded!\n" + res = "PASS" + elif results == WARNINGS: + status = "Build Had Warnings%s\n" % t + res = "WARN" + else: + status = "BUILD FAILED%s\n" % t + res = "FAIL" + + if build.getLogs(): + log = build.getLogs()[-1] + laststep = log.getStep().getName() + lastlog = log.getText() + + # only give me the last lines of the log files. + lines = re.split('\n', lastlog) + lastlog = '' + for logline in lines[max(0, len(lines)-100):]: + lastlog = lastlog + logline + + # TODO: it would be nice to provide a URL for the specific build + # here. That involves some coordination with html.Waterfall . + # Ideally we could do: + # helper = self.parent.getServiceNamed("html") + # if helper: + # url = helper.getURLForBuild(build) + + text = self.body % { 'result': res, + 'builder': name, + 'revision': revision, + 'branch': ss.branch, + 'blamelist': ",".join(build.getResponsibleUsers()), + 'buildurl': buildurl, + 'buildboturl': buildboturl, + 'reason': buildreason, + 'source': source, + 'intro': intro, + 'status': status, + 'slave': buildslave, + 'laststep': laststep, + 'lastlog': lastlog, + } + + haveAttachments = False + if ss.patch or self.addLogs: + haveAttachments = True + if not canDoAttachments: + log.msg("warning: I want to send mail with attachments, " + "but this python is too old to have " + "email.MIMEMultipart . Please upgrade to python-2.3 " + "or newer to enable addLogs=True") + + if haveAttachments and canDoAttachments: + m = MIMEMultipart() + m.attach(MIMEText(text)) + else: + m = Message() + m.set_payload(text) + + m['Date'] = formatdate(localtime=True) + m['Subject'] = self.subject % { 'result': res, + 'builder': name, + 'revision': revision, + 'branch': ss.branch + } + m['From'] = self.fromaddr + # m['To'] is added later + m['Reply-To'] = self.replytoaddr + + if ss.patch: + a = MIMEText(patch) + a.add_header('Content-Disposition', "attachment", + filename="source patch") + m.attach(a) + if self.addLogs: + for log in build.getLogs(): + name = "%s.%s" % (log.getStep().getName(), + log.getName()) + a = MIMEText(log.getText()) + a.add_header('Content-Disposition', "attachment", + filename=name) + m.attach(a) + + # now, who is this message going to? + dl = [] + recipients = self.extraRecipients[:] + if self.sendToInterestedUsers and self.lookup: + for u in build.getInterestedUsers(): + d = defer.maybeDeferred(self.lookup.getAddress, u) + d.addCallback(recipients.append) + dl.append(d) + d = defer.DeferredList(dl) + d.addCallback(self._gotRecipients, recipients, m) + return d + |