("test", "make test", "text/plain"), ],
}
-retry_task = [ ( "retry",
- '''set -e
- git remote add -t master master %s
- git fetch master
- while :; do
- sleep 60
- git describe master/master > old_master.desc
- git fetch master
- git describe master/master > master.desc
- diff old_master.desc master.desc
- done
- ''' % samba_master, "test/plain" ) ]
-
def run_cmd(cmd, dir=None, show=None, output=False, checkfail=True, shell=False):
if show is None:
run_cmd(["git", "checkout", revision])
+class RetryChecker(object):
+ """Check whether it is necessary to retry."""
+
+ def __init__(self, dir):
+ run_cmd(["git", "remote", "add", "-t", "master", "master", samba_master])
+ run_cmd(["git", "fetch", "master"])
+ cmd = '''set -e
+ while :; do
+ sleep 60
+ git describe master/master > old_master.desc
+ git fetch master
+ git describe master/master > master.desc
+ diff old_master.desc master.desc
+ done
+ '''
+ self.proc = Popen(cmd, shell=True, cwd=self.dir)
+
+ def poll(self):
+ return self.proc.poll()
+
+ def kill(self):
+ self.proc.terminate()
+ self.proc.wait()
+ self.retry.proc = None
+
+
class TreeStageBuilder(object):
"""Handle building of a particular stage for a tree.
"""
self.name = name
self.command = command
self.fail_quickly = fail_quickly
- self.status = None
+ self.exitcode = None
self.stdin = open(os.devnull, 'r')
def start(self):
raise NotImplementedError(self.start)
def poll(self):
- self.status = self.proc.poll()
- return self.status
+ self.exitcode = self.proc.poll()
+ return self.exitcode
def kill(self):
if self.proc is not None:
@property
def failure_reason(self):
- return "failed '%s' with status %d" % (self.cmd, self.status)
+ raise NotImplementedError(self.failure_reason)
@property
def failed(self):
- return (os.WIFSIGNALED(self.status) or os.WEXITSTATUS(self.status) != 0)
+ return (self.exitcode != 0)
class PlainTreeStageBuilder(TreeStageBuilder):
stdout=self.tree.stdout, stderr=self.tree.stderr,
stdin=self.stdin)
+ @property
+ def failure_reason(self):
+ return "failed '%s' with exit code %d" % (self.command, self.exitcode)
+
class AbortingTestResult(subunithelper.TestsuiteEnabledTestResult):
self.stage.proc.terminate()
+class FailureTrackingTestResult(subunithelper.TestsuiteEnabledTestResult):
+
+ def __init__(self, stage):
+ super(FailureTrackingTestResult, self).__init__()
+ self.stage = stage
+
+ def addError(self, test, details=None):
+ if self.stage.failed_test is None:
+ self.stage.failed_test = ("error", test)
+
+ def addFailure(self, test, details=None):
+ if self.stage.failed_test is None:
+ self.stage.failed_test = ("failure", test)
+
+
class SubunitTreeStageBuilder(TreeStageBuilder):
def __init__(self, tree, name, command, fail_quickly=False):
super(SubunitTreeStageBuilder, self).__init__(tree, name, command,
fail_quickly)
- self.failed_tests = []
+ self.failed_test = None
self.subunit_path = os.path.join(gitroot,
"%s.%s.subunit" % (self.tree.tag, self.name))
self.tree.logfiles.append(
self.subunit = open(self.subunit_path, 'w')
formatter = subunithelper.PlainFormatter(False, True, {})
- clients = [formatter, subunit.TestProtocolClient(self.subunit)]
+ clients = [formatter, subunit.TestProtocolClient(self.subunit),
+ FailureTrackingTestResult(self)]
if fail_quickly:
clients.append(AbortingTestResult(self))
self.subunit_server = subunit.TestProtocolServer(
else:
buffered += l
self.buffered = buffered
- self.status = self.proc.poll()
- if self.status is not None:
+ self.exitcode = self.proc.poll()
+ if self.exitcode is not None:
self.subunit.close()
- return self.status
+ return self.exitcode
+
+ @property
+ def failure_reason(self):
+ if self.failed_test:
+ return "failed '%s' with %s in test %s" (self.command, self.failed_test[0], self.failed_test[1])
+ else:
+ return "failed '%s' with exit code %d in unknown test" % (self.command, self.exitcode)
class TreeBuilder(object):
self.tag = self.name.replace('/', '_')
self.sequence = sequence
self.next = 0
+ self.stages = []
self.stdout_path = os.path.join(gitroot, "%s.stdout" % (self.tag, ))
self.stderr_path = os.path.join(gitroot, "%s.stderr" % (self.tag, ))
self.logfiles = [
run_cmd(["rm", "-rf", self.sdir])
clone_gitroot(self.sdir, revision)
self.start_next()
+ self.exitcode = None
def start_next(self):
if self.next == len(self.sequence):
(stage_name, cmd, output_mime_type) = self.sequence[self.next]
cmd = cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
if output_mime_type == "text/plain":
- self.stage = PlainTreeStageBuilder(self, stage_name, cmd,
+ self.current_stage = PlainTreeStageBuilder(self, stage_name, cmd,
self.fail_quickly)
elif output_mime_type == "text/x-subunit":
- self.stage = SubunitTreeStageBuilder(self, stage_name, cmd,
+ self.current_stage = SubunitTreeStageBuilder(self, stage_name, cmd,
self.fail_quickly)
else:
raise Exception("Unknown output mime type %s" % output_mime_type)
- self.stage.start()
+ self.stages.append(self.current_stage)
+ self.current_stage.start()
self.next += 1
def remove_logs(self):
for path, name, mime_type in self.logfiles:
os.unlink(path)
- @property
- def status(self):
- return self.stage.status
-
def poll(self):
- return self.stage.poll()
+ self.exitcode = self.current_stage.poll()
+ if self.exitcode is not None:
+ self.current_stage = None
+ return self.exitcode
def kill(self):
- if self.stage is not None:
- self.stage.kill()
- self.stage = None
+ if self.current_stage is not None:
+ self.current_stage.kill()
+ self.current_stage = None
@property
def failed(self):
- if self.stage is None:
- return False
- return self.stage.failed
+ return any([s.failed for s in self.stages])
+
+ @property
+ def failed_stage(self):
+ for s in self.stages:
+ if s.failed:
+ return s
+ return s
@property
def failure_reason(self):
- return "%s: [%s] %s" % (self.name, self.stage.name,
- self.stage.failure_reason)
+ return "%s: [%s] %s" % (self.name, self.failed_stage.name,
+ self.failed_stage.failure_reason)
class BuildList(object):
b = TreeBuilder(n, tasks[n], not options.fail_slowly)
self.tlist.append(b)
if options.retry:
- self.retry = TreeBuilder('retry', retry_task,
- not options.fail_slowly)
+ self.retry = RetryChecker(self.sdir)
self.need_retry = False
def kill_kids(self):
self.tail_proc.wait()
self.tail_proc = None
if self.retry is not None:
- self.retry.proc.terminate()
- self.retry.proc.wait()
- self.retry = None
+ self.retry.kill()
for b in self.tlist:
b.kill()
while True:
none_running = True
for b in self.tlist:
- if b.stage is None:
+ if b.current_stage is None:
continue
none_running = False
if b.poll() is None:
continue
- b.stage = None
return b
if options.retry:
- ret = self.retry.proc.poll()
- if ret is not None:
+ ret = self.retry.poll()
+ if ret:
self.need_retry = True
self.retry = None
return None
break
if b.failed:
self.kill_kids()
- return (b.status, b.name, b.stage, b.tag, b.failure_reason)
+ return (b.exitcode, b.name, b.failed_stage, b.tag, b.failure_reason)
b.start_next()
self.kill_kids()
return (0, None, None, None, "All OK")
action="store_true")
-def email_failure(blist, status, failed_task, failed_stage, failed_tag, errstr):
+def email_failure(blist, exitcode, failed_task, failed_stage, failed_tag, errstr):
'''send an email to options.email about the failure'''
user = os.getenv("USER")
text = '''
msg = MIMEMultipart()
msg['Subject'] = 'autobuild failure for task %s during %s' % (
- failed_task, failed_stage)
+ failed_task, failed_stage.name)
msg['From'] = 'autobuild@samba.org'
msg['To'] = options.email
blist = BuildList(tasks, args)
if options.tail:
blist.start_tail()
- (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
- if status != 0 or errstr != "retry":
+ (exitcode, failed_task, failed_stage, failed_tag, errstr) = blist.run()
+ if exitcode != 0 or errstr != "retry":
break
cleanup()
except:
print("waiting for tail to flush")
time.sleep(1)
-if status == 0:
+if exitcode == 0:
print errstr
if options.passcmd is not None:
print("Running passcmd: %s" % options.passcmd)
blist.tarlogs("logs.tar.gz")
if options.email is not None:
- email_failure(blist, status, failed_task, failed_stage, failed_tag,
+ email_failure(blist, exitcode, failed_task, failed_stage, failed_tag,
errstr)
cleanup()
print(errstr)
print("Logs in logs.tar.gz")
-sys.exit(status)
+sys.exit(exitcode)