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