3
# Licensed to the Apache Software Foundation (ASF) under one
4
# or more contributor license agreements. See the NOTICE file
5
# distributed with this work for additional information
6
# regarding copyright ownership. The ASF licenses this file
7
# to you under the Apache License, Version 2.0 (the
8
# "License"); you may not use this file except in compliance
9
# with the License. You may obtain a copy of the License at
11
# http://www.apache.org/licenses/LICENSE-2.0
13
# Unless required by applicable law or agreed to in writing,
14
# software distributed under the License is distributed on an
15
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
# KIND, either express or implied. See the License for the
17
# specific language governing permissions and limitations
25
from email.Message import Message
26
from email.Utils import formatdate
27
from email.MIMEText import MIMEText
29
from twisted.internet import defer
30
from twisted.application import service
32
from buildbot.status.builder import FAILURE, SUCCESS, WARNINGS
33
from buildbot.status.mail import MailNotifier
35
class SVNMailNotifier(MailNotifier):
36
"""Implement custom status mails for the Subversion project"""
38
def __init__(self, fromaddr, mode="all", categories=None, builders=None,
39
addLogs=False, relayhost="localhost",
40
subject="buildbot %(result)s in %(builder)s",
41
lookup=None, extraRecipients=[],
42
sendToInterestedUsers=True,
47
@param body: a string to be used as the body of the message.
49
@type replytoaddr: string
50
@param replytoaddr: the email address to be used in the 'Reply-To' header.
54
self.replytoaddr = replytoaddr
56
# pass the rest of the parameters to our parent.
57
MailNotifier.__init__(self, fromaddr, mode, categories, builders,
58
addLogs, relayhost, subject, lookup, extraRecipients,
59
sendToInterestedUsers)
61
def buildMessage(self, name, build, results):
62
if self.mode == "all":
63
intro = "The Buildbot has finished a build of %s.\n" % name
64
elif self.mode == "failing":
65
intro = "The Buildbot has detected a failed build of %s.\n" % name
67
intro = "The Buildbot has detected a new failure of %s.\n" % name
70
buildurl = self.status.getURLForThing(build)
71
# lgo: url's are already quoted now.
73
# buildurl = urllib.quote(buildurl, '/:')
76
buildboturl = self.status.getBuildbotURL()
78
# buildboturl = urllib.quote(url, '/:')
81
buildreason = build.getReason()
85
ss = build.getSourceStamp()
87
source = "unavailable"
89
if build.getChanges():
90
revision = max([int(c.revision) for c in build.getChanges()])
95
source += "[branch %s] " % ss.branch
97
source += str(revision)
100
if ss.patch is not None:
101
source += " (plus patch)"
104
buildslave = build.getSlavename()
106
# TODO: maybe display changes here? or in an attachment?
111
t = ": " + " ".join(t)
115
if results == SUCCESS:
116
status = "Build succeeded!\n"
118
elif results == WARNINGS:
119
status = "Build Had Warnings%s\n" % t
122
status = "BUILD FAILED%s\n" % t
126
log = build.getLogs()[-1]
127
laststep = log.getStep().getName()
128
lastlog = log.getText()
130
# only give me the last lines of the log files.
131
lines = re.split('\n', lastlog)
133
for logline in lines[max(0, len(lines)-100):]:
134
lastlog = lastlog + logline
136
# TODO: it would be nice to provide a URL for the specific build
137
# here. That involves some coordination with html.Waterfall .
138
# Ideally we could do:
139
# helper = self.parent.getServiceNamed("html")
141
# url = helper.getURLForBuild(build)
143
text = self.body % { 'result': res,
145
'revision': revision,
147
'blamelist': ",".join(build.getResponsibleUsers()),
148
'buildurl': buildurl,
149
'buildboturl': buildboturl,
150
'reason': buildreason,
155
'laststep': laststep,
159
haveAttachments = False
160
if ss.patch or self.addLogs:
161
haveAttachments = True
162
if not canDoAttachments:
163
log.msg("warning: I want to send mail with attachments, "
164
"but this python is too old to have "
165
"email.MIMEMultipart . Please upgrade to python-2.3 "
166
"or newer to enable addLogs=True")
168
if haveAttachments and canDoAttachments:
170
m.attach(MIMEText(text))
175
m['Date'] = formatdate(localtime=True)
176
m['Subject'] = self.subject % { 'result': res,
178
'revision': revision,
181
m['From'] = self.fromaddr
182
# m['To'] is added later
183
m['Reply-To'] = self.replytoaddr
187
a.add_header('Content-Disposition', "attachment",
188
filename="source patch")
191
for log in build.getLogs():
192
name = "%s.%s" % (log.getStep().getName(),
194
a = MIMEText(log.getText())
195
a.add_header('Content-Disposition', "attachment",
199
# now, who is this message going to?
201
recipients = self.extraRecipients[:]
202
if self.sendToInterestedUsers and self.lookup:
203
for u in build.getInterestedUsers():
204
d = defer.maybeDeferred(self.lookup.getAddress, u)
205
d.addCallback(recipients.append)
207
d = defer.DeferredList(dl)
208
d.addCallback(self._gotRecipients, recipients, m)