def buildMessage(self, builder_name, build_status, results, step_name): """Send an email about the tree closing. Don't attach the patch as MailNotifier.buildMessage does. @type builder_name: string @type build_status: L{buildbot.status.builder.BuildStatus} @type step_name: name of this step """ # TODO(maruel): Update function signature to match # mail.MailNotifier.buildMessage(). if (self._last_time_mail_sent and self._last_time_mail_sent > time.time() - self.minimum_delay_between_alert): # Rate limit tree alerts. log.msg('Suppressing repeat email') return log.msg('About to email') self._last_time_mail_sent = time.time() # TODO(maruel): Use self.createEmail(). blame_interested_users = self.shouldBlameCommitters(step_name) project_name = self.master_status.getTitle() revisions_list = build_utils.getAllRevisions(build_status) build_url = self.master_status.getURLForThing(build_status) waterfall_url = self.master_status.getBuildbotURL() status_text = self.status_header % { 'buildbotURL': waterfall_url, 'builder': builder_name, 'builderName': builder_name, 'buildProperties': build_status.getProperties(), 'buildURL': build_url, 'project': project_name, 'reason': build_status.getReason(), 'slavename': build_status.getSlavename(), 'steps': step_name, } # Use the first line as a title. status_title = status_text.split('\n', 1)[0] blame_list = ','.join(build_status.getResponsibleUsers()) revisions_string = '' latest_revision = 0 if revisions_list: revisions_string = ', '.join([str(rev) for rev in revisions_list]) latest_revision = max([rev for rev in revisions_list]) if results[0] == FAILURE: result = 'failure' else: result = 'warning' # Generate a HTML table looking like the waterfall. # WARNING: Gmail ignores embedded CSS style. I don't know how to fix that so # meanwhile, I just won't embedded the CSS style. html_content = ( """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>%s</title> </head> <body> <a href="%s">%s</a><p> %s<p> <a href="%s">%s</a><p> Revision: %s<br> """ % (status_title, waterfall_url, waterfall_url, status_text.replace('\n', "<br>\n"), build_url, build_url, revisions_string)) # Only include the blame list if relevant. if blame_interested_users: html_content += " Blame list: %s<p>\n" % blame_list html_content += build_utils.EmailableBuildTable( build_status, waterfall_url) html_content += "<p>" # Add the change list descriptions. getChanges() returns a tuple of # buildbot.changes.changes.Change for change in build_status.getChanges(): html_content += change.asHTML() html_content += "</body>\n</html>" # Simpler text content for non-html aware clients. text_content = ("""%s %s %swaterfall?builder=%s --=> %s <=-- Revision: %s Blame list: %s Buildbot waterfall: http://build.chromium.org/ """ % (status_title, build_url, urllib.quote(waterfall_url, '/:'), urllib.quote(builder_name), status_text, revisions_string, blame_list)) m = MIMEMultipart('alternative') # The HTML message, is best and preferred. m.attach(MIMEText(text_content, 'plain', 'iso-8859-1')) m.attach(MIMEText(html_content, 'html', 'iso-8859-1')) m['Date'] = formatdate(localtime=True) m['Subject'] = self.subject % { 'result': result, 'projectName': project_name, 'builder': builder_name, 'reason': build_status.getReason(), 'revision': str(latest_revision), 'buildnumber': str(build_status.getNumber()), 'date': str(datetime.date.today()), 'steps': step_name, 'slavename': build_status.getSlavename(), } m['From'] = self.fromaddr if self.reply_to: m['Reply-To'] = self.reply_to recipients = list(self.extraRecipients[:]) if self.sheriffs: recipients.extend( BuildSheriffs.GetSheriffs(classes=self.sheriffs, data_dir=self.public_html)) dl = [] if self.sendToInterestedUsers and self.lookup and blame_interested_users: for u in build_status.getInterestedUsers(): d = defer.maybeDeferred(self.lookup.getAddress, u) d.addCallback(recipients.append) dl.append(d) defered_object = defer.DeferredList(dl) if not self.enable_mail: defered_object.addCallback(self._logMail, recipients, m) else: defered_object.addCallback(self._gotRecipients, recipients, m) defered_object.addCallback(self.getFinishedMessage, builder_name, build_status, step_name) return defered_object
def buildMessage(self, name, build, results): """Send an email about the result. Send it as a nice HTML message.""" log.msg('Building try job email') projectName = self.master_status.getTitle() if len(build) != 1: # TODO(maruel): Panic or process them all. pass build = build[0] job_stamp = build.getSourceStamp() build_url = self.master_status.getURLForThing(build) waterfall_url = self.master_status.getBuildbotURL() if results == SUCCESS: status_text_html = "You are awesome! Try succeeded!" res = "success" elif results == WARNINGS: status_text_html = "Try Had Warnings" res = "warnings" else: status_text_html = self.failure_message if status_text_html is None: status_text_html = ( 'TRY FAILED<p>' '<strong>If you think the try slave is broken (it happens!),' 'please REPLY to this email, don\'t ask on irc, mailing ' 'list or IM.<br>' 'If you think the test is flaky, notify the sheriffs.</strong><br>' 'Please use "rich text" replies so the links aren\'t lost.<br>' 'It is possible that you get no reply, don\'t worry, the reply ' 'address isn\'t a blackhole.' '<p>' 'Thanks!') res = "failure" info = { 'result': res, 'projectName': projectName, 'builder': name, 'reason': build.getReason(), 'revision': job_stamp.revision, 'timestamp': getattr(job_stamp, "timestamp", "") } subject = self.subject % info first_line = ( "try %(result)s for %(reason)s on %(builder)s @ r%(revision)s" % info) html_params = { 'subject': subject, 'first_line': first_line, 'waterfall_url': waterfall_url, 'status_text_html': status_text_html, 'build_url': build_url, 'slave': build.getSlavename(), } # Generate a HTML table looking like the waterfall. # WARNING: Gmail ignores embedded CSS style unless it's inline. html_content = ( """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>%(subject)s</title> </head> <body style="font-family: Verdana, Cursor; font-size: 10px;"> %(first_line)s<p> <a href="%(waterfall_url)s">%(waterfall_url)s</a><p> %(status_text_html)s<p> <a href="%(build_url)s">%(build_url)s</a><br> slave: %(slave)s<br> """) % html_params html_content += build_utils.EmailableBuildTable(build, waterfall_url) footer = self.footer if self.footer is None: footer = """<br> FAQ: <a href="http://sites.google.com/a/chromium.org/dev/developers/testing/try-server-usage"> http://sites.google.com/a/chromium.org/dev/developers/testing/try-server-usage</a><br> </body> </html> """ html_content += footer m = MIMEMultipart() m.attach(MIMEText(html_content, 'html', 'iso-8859-1')) m['Date'] = formatdate(localtime=True) m['Subject'] = subject m['From'] = self.fromaddr if self.reply_to: m['Reply-To'] = self.reply_to # 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
def buildMessage_internal(self, name, build, results): """Send an email about the result. Send it as a nice HTML message.""" if results == SUCCESS and name in self.no_email_on_success: log.msg('Skipping success email for %s' % name) return log.msg('Building try job email') projectName = self.master_status.getTitle() if len(build) != 1: # TODO(maruel): Panic or process them all. return build = build[0] job_stamp = build.getSourceStamp() build_url = self.master_status.getURLForThing(build) builder_url = self.master_status.getURLForThing(build.getBuilder()) waterfall_url = self.master_status.getBuildbotURL() if results == SUCCESS: status_text_html = "You are awesome! Try succeeded!" res = "success" elif results == WARNINGS: status_text_html = "Try Had Warnings" res = "warnings" else: status_text_html = self.failure_message if status_text_html is None: status_text_html = ( 'TRY FAILED<p>' '<strong>If the failure is unrelated to your change, the test may' ' be flaky. Contact the sheriffs via irc or gchat, or email the' ' team mailing list.<br> If you think the system is broken,' ' please reply to this email.</strong><br>' ' Common examples of system issues: sync or compile failures' ' on a specific machine, or failures that only affect trybots.<br>' 'Please use "rich text" replies so the links aren\'t lost.' ' If you do not receive a reply, don\'t worry. The reply address' ' isn\'t a blackhole.<p>Thanks!') res = "failure" info = { 'result': res, 'projectName': projectName, 'builder': name, 'reason': build.getReason(), 'revision': build.getProperties().getProperty('got_revision', job_stamp.revision), 'timestamp': getattr(job_stamp, "timestamp", "") } if self.get_info: info = self.get_info(info, build) subject = self.subject % info first_line = ( "try %(result)s for %(reason)s on %(builder)s @ r%(revision)s" % info) build_props = build.getProperties() if build_props.getProperty('requester') == '*****@*****.**': # CQ handles notifying people about the ultimate success/failure of # tryjobs by posting to rietveld. It also generates a LOT of email # from the tryserver which is noisy. return parent_html = '' if build_props: parent_name = build_props.getProperty('parent_buildername') parent_buildnum = build_props.getProperty('parent_buildnumber') if parent_name and parent_buildnum: parent_builder_url = ('%s/builders/%s' % (waterfall_url.rstrip('/'), parent_name)) parent_build = parent_builder_url + '/builds/%s' % parent_buildnum parent_html = ( '<br>Parent build: <a href="%(build_url)s">%(buildnum)s</a>' ' on <a href="%(builder_url)s">%(builder)s</a><br>' % { 'builder_url': parent_builder_url, 'builder': parent_name, 'build_url': parent_build, 'buildnum': parent_buildnum }) slave = build.getSlavename() slave_url = '%s/buildslaves/%s' % (waterfall_url.rstrip('/'), slave) html_params = { 'subject': subject, 'first_line': first_line, 'waterfall_url': waterfall_url, 'status_text_html': status_text_html, 'build_url': build_url, 'parent_builder_html': parent_html, 'slave': slave, 'slave_url': slave_url, 'build_number': build.getNumber(), 'builder': build.getBuilder().getName(), 'builder_url': builder_url, } # Generate a HTML table looking like the waterfall. # WARNING: Gmail ignores embedded CSS style unless it's inline. html_content = ( """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>%(subject)s</title> </head> <body style="font-family: Verdana, Cursor; font-size: 10px;"> %(first_line)s<p> <a href="%(waterfall_url)s">%(waterfall_url)s</a><p> %(status_text_html)s<p> %(parent_builder_html)s Build: <a href="%(build_url)s">%(build_number)s</a> on <a href="%(builder_url)s">%(builder)s</a><br> slave: <a href="%(slave_url)s">%(slave)s</a><br> """) % html_params html_content += self.SpecialPropertiesAsHTML(build_props.asDict()) html_content += build_utils.EmailableBuildTable(build, waterfall_url) footer = self.footer if self.footer is None: footer = """<br> FAQ: <a href="https://sites.google.com/a/chromium.org/dev/developers/testing/try-server-usage"> https://sites.google.com/a/chromium.org/dev/developers/testing/try-server-usage</a><br> </body> </html> """ html_content += footer m = MIMEMultipart() m.attach(MIMEText(html_content, 'html', 'iso-8859-1')) m['Date'] = formatdate(localtime=True) m['Subject'] = subject m['From'] = self.fromaddr return m