1
2 from email.Message import Message
3 from email.Utils import formatdate
4
5 from zope.interface import implements
6 from twisted.internet import defer
7
8 from buildbot import interfaces
9 from buildbot.status import mail
10 from buildbot.status.builder import SUCCESS, WARNINGS
11 from buildbot.steps.shell import WithProperties
12
13 import zlib, bz2, base64
14
15
16
17
19 """This is a Tinderbox status notifier. It can send e-mail to a number of
20 different tinderboxes or people. E-mails are sent at the beginning and
21 upon completion of each build. It can be configured to send out e-mails
22 for only certain builds.
23
24 The most basic usage is as follows::
25 TinderboxMailNotifier(fromaddr="buildbot@localhost",
26 tree="MyTinderboxTree",
27 extraRecipients=["tinderboxdaemon@host.org"])
28
29 The builder name (as specified in master.cfg) is used as the "build"
30 tinderbox option.
31
32 """
33 implements(interfaces.IEmailSender)
34
35 compare_attrs = ["extraRecipients", "fromaddr", "categories", "builders",
36 "addLogs", "relayhost", "subject", "binaryURL", "tree",
37 "logCompression", "errorparser", "columnName",
38 "useChangeTime"]
39
40 - def __init__(self, fromaddr, tree, extraRecipients,
41 categories=None, builders=None, relayhost="localhost",
42 subject="buildbot %(result)s in %(builder)s", binaryURL="",
43 logCompression="", errorparser="unix", columnName=None,
44 useChangeTime=False):
45 """
46 @type fromaddr: string
47 @param fromaddr: the email address to be used in the 'From' header.
48
49 @type tree: string
50 @param tree: The Tinderbox tree to post to.
51
52 @type extraRecipients: tuple of string
53 @param extraRecipients: E-mail addresses of recipients. This should at
54 least include the tinderbox daemon.
55
56 @type categories: list of strings
57 @param categories: a list of category names to serve status
58 information for. Defaults to None (all
59 categories). Use either builders or categories,
60 but not both.
61
62 @type builders: list of strings
63 @param builders: a list of builder names for which mail should be
64 sent. Defaults to None (send mail for all builds).
65 Use either builders or categories, but not both.
66
67 @type relayhost: string
68 @param relayhost: the host to which the outbound SMTP connection
69 should be made. Defaults to 'localhost'
70
71 @type subject: string
72 @param subject: a string to be used as the subject line of the message.
73 %(builder)s will be replaced with the name of the
74 %builder which provoked the message.
75 This parameter is not significant for the tinderbox
76 daemon.
77
78 @type binaryURL: string
79 @param binaryURL: If specified, this should be the location where final
80 binary for a build is located.
81 (ie. http://www.myproject.org/nightly/08-08-2006.tgz)
82 It will be posted to the Tinderbox.
83
84 @type logCompression: string
85 @param logCompression: The type of compression to use on the log.
86 Valid options are"bzip2" and "gzip". gzip is
87 only known to work on Python 2.4 and above.
88
89 @type errorparser: string
90 @param errorparser: The error parser that the Tinderbox server
91 should use when scanning the log file.
92 Default is "unix".
93
94 @type columnName: string
95 @param columnName: When columnName is None, use the buildername as
96 the Tinderbox column name. When columnName is a
97 string this exact string will be used for all
98 builders that this TinderboxMailNotifier cares
99 about (not recommended). When columnName is a
100 WithProperties instance it will be interpolated
101 as such. See WithProperties for more detail.
102 @type useChangeTime: bool
103 @param useChangeTime: When True, the time of the first Change for a
104 build is used as the builddate. When False,
105 the current time is used as the builddate.
106 """
107
108 mail.MailNotifier.__init__(self, fromaddr, categories=categories,
109 builders=builders, relayhost=relayhost,
110 subject=subject,
111 extraRecipients=extraRecipients,
112 sendToInterestedUsers=False)
113 self.tree = tree
114 self.binaryURL = binaryURL
115 self.logCompression = logCompression
116 self.errorparser = errorparser
117 self.useChangeTime = useChangeTime
118 assert columnName is None or type(columnName) is str \
119 or isinstance(columnName, WithProperties), \
120 "columnName must be None, a string, or a WithProperties instance"
121 self.columnName = columnName
122
131
133 text = ""
134 res = ""
135
136 t = "tinderbox:"
137
138 text += "%s tree: %s\n" % (t, self.tree)
139
140
141 builddate = int(build.getTimes()[0])
142
143
144 if self.useChangeTime:
145 try:
146 builddate = build.getChanges()[-1].when
147 except:
148 pass
149 text += "%s builddate: %s\n" % (t, builddate)
150 text += "%s status: " % t
151
152 if results == "building":
153 res = "building"
154 text += res
155 elif results == SUCCESS:
156 res = "success"
157 text += res
158 elif results == WARNINGS:
159 res = "testfailed"
160 text += res
161 else:
162 res += "busted"
163 text += res
164
165 text += "\n";
166
167 if self.columnName is None:
168
169 text += "%s build: %s\n" % (t, name)
170 elif type(self.columnName) is str:
171
172 text += "%s build: %s\n" % (t, self.columnName)
173 elif isinstance(self.columnName, WithProperties):
174
175 text += "%s build: %s\n" % (t, build.getProperties().render(self.columnName))
176 else:
177 raise Exception("columnName is an unhandled value")
178 text += "%s errorparser: %s\n" % (t, self.errorparser)
179
180
181 if results == "building":
182 text += "%s END\n" % t
183
184 else:
185 text += "%s binaryurl: %s\n" % (t, self.binaryURL)
186 text += "%s logcompression: %s\n" % (t, self.logCompression)
187
188
189 logEncoding = ""
190 tinderboxLogs = ""
191 for log in build.getLogs():
192 l = ""
193 if self.logCompression == "bzip2":
194 compressedLog = bz2.compress(log.getText())
195 l = base64.encodestring(compressedLog)
196 logEncoding = "base64";
197 elif self.logCompression == "gzip":
198 compressedLog = zlib.compress(log.getText())
199 l = base64.encodestring(compressedLog)
200 logEncoding = "base64";
201 else:
202 l = log.getText()
203 tinderboxLogs += l
204
205 text += "%s logencoding: %s\n" % (t, logEncoding)
206 text += "%s END\n\n" % t
207 text += tinderboxLogs
208 text += "\n"
209
210 m = Message()
211 m.set_payload(text)
212
213 m['Date'] = formatdate(localtime=True)
214 m['Subject'] = self.subject % { 'result': res,
215 'builder': name,
216 }
217 m['From'] = self.fromaddr
218
219
220 d = defer.DeferredList([])
221 d.addCallback(self._gotRecipients, self.extraRecipients, m)
222 return d
223