import sys from os import environ import base64 from smtplib import SMTP from StringIO import StringIO from time import time import bz2 from tempfile import TemporaryFile import killableprocess import subprocess tinderplatform = (sys.platform == "win32") and "windows" or "unix" moduleversion = "tinderclient.py: $Revision$" def TinderBuild(buildname, buildfunc, administrator, tree="MozillaTest", smtpserver="localhost", smtpuser=None, smtppasswd=None, start_time=time(), tinderbox="tinderbox-daemon@tinderbox.mozilla.org" ): r"""Send a tinderbox-started mail, perform a build action, and report the results of the build. buildname should be a string: the name of the build as it will appear on the tinderbox. buildfunc is a callable which will perform the build actions. If an exception is thrown during the build, the build will be reported as a failure. Otherwise the function should return one of 'success', 'testfailed', or 'busted'. The callable is called with one parameter, a file-like object to which logging should be written. tree indicates on which tree the tinderbox should appear. administrator is the email of the local administrator for this tinderbox client, and is the From: address of the tinderbox emails smtpserver, smtpuser, smtppasswd configure the SMTP mailer for the status emails. start_time is the timestamp that the tinderbox says it is starting at. Although this is typically "now", some kinds of tinderboxes such as testing and l10n machines may wish to lie about it. tinderbox is the tinderbox server email. """ def send_message(tinderheaders, log=None): msgl = [("From: %s\r\nTo: %s\r\n\r\n" % (administrator, tinderbox))] msgl.append("\n") for header in tinderheaders.iteritems(): msgl.append("tinderbox: %s: %s\n" % header) msgl.append("tinderbox: END\n\n") if log: log = log.replace("\r\n", "\n") compressed = bz2.compress(log) msgl.append(base64.encodestring(compressed)) svr = SMTP(smtpserver) if smtpuser: svr.login(smtpuser, smtppasswd) svr.sendmail(administrator, tinderbox, "".join(msgl)) svr.quit() tinderheaders = { 'tree': tree, 'builddate': int(start_time), 'status': 'building', 'build': buildname, 'errorparser': tinderplatform, 'buildfamily': tinderplatform, 'version': moduleversion } print "Sending start message to %s." % tinderbox send_message(tinderheaders) tinderheaders.update({ 'logcompression': 'bzip2', 'logencoding': 'base64' }) logfile = TemporaryFile(bufsize=0) try: print "Starting build function." try: tinderheaders['status'] = buildfunc(logfile) if tinderheaders['status'] not in ("busted", "testfailed", "success"): raise Exception("Build function returned unrecognized status: '%s'" % tinderheaders['status']) except Exception, e: tinderheaders['status'] = 'busted' print >>logfile, e raise finally: print "Sending end message to %s." % tinderbox logfile.seek(0) send_message(tinderheaders, logfile.read()) return tinderheaders['status'] def log_command(command, log, **kwargs): print "Running: %s" % " ".join(command) print >>log, " ".join(command) killableprocess.check_call(command, stdout=log, stderr=subprocess.STDOUT, **kwargs)