2 # run tests on all Samba subprojects and push to a git tree on success
3 # Copyright Andrew Tridgell 2010
4 # released under GNU GPL v3 or later
6 from subprocess
import call
, check_call
,Popen
, PIPE
7 import os
, tarfile
, sys
, time
8 from optparse
import OptionParser
11 from email
.mime
.text
import MIMEText
12 from email
.mime
.base
import MIMEBase
13 from email
.mime
.application
import MIMEApplication
14 from email
.mime
.multipart
import MIMEMultipart
15 from distutils
.sysconfig
import get_python_lib
18 # This speeds up testing remarkably.
19 os
.environ
['TDB_NO_FSYNC'] = '1'
30 "talloc" : "lib/talloc",
31 "replace" : "lib/replace",
32 "tevent" : "lib/tevent",
39 defaulttasks
= [ "ctdb", "samba", "samba-ctdb", "samba-libs", "ldb", "tdb", "talloc", "replace", "tevent", "pidl" ]
42 "ctdb" : [ ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
43 ("configure", "./configure ${PREFIX}", "text/plain"),
44 ("make", "make all", "text/plain"),
45 ("install", "make install", "text/plain"),
46 ("test", "make autotest", "text/plain"),
47 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
48 ("clean", "make clean", "text/plain") ],
50 # We have 'test' before 'install' because, 'test' should work without 'install'
51 "samba" : [ ("configure", "./configure.developer --picky-developer ${PREFIX} --with-selftest-prefix=./bin/ab --with-profiling-data", "text/plain"),
52 ("make", "make -j", "text/plain"),
53 ("test", "make test FAIL_IMMEDIATELY=1", "text/plain"),
54 ("install", "make install", "text/plain"),
55 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
56 ("clean", "make clean", "text/plain") ],
58 "samba-ctdb" : [ ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
60 # make sure we have tdb around:
61 ("tdb-configure", "cd lib/tdb && PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig ./configure --bundled-libraries=NONE --abi-check --enable-debug -C ${PREFIX}", "text/plain"),
62 ("tdb-make", "cd lib/tdb && make", "text/plain"),
63 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
66 # build samba with cluster support (also building ctdb):
67 ("samba-configure", "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH PKG_CONFIG_PATH=${PREFIX_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH} ./configure.developer --picky-developer ${PREFIX} --with-selftest-prefix=./bin/ab --with-cluster-support --bundled-libraries=!tdb", "text/plain"),
68 ("samba-make", "make", "text/plain"),
69 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
70 ("samba-install", "make install", "text/plain"),
71 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
74 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
75 ("clean", "make clean", "text/plain"),
76 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain") ],
79 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
80 ("talloc-configure", "cd lib/talloc && PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig ./configure --bundled-libraries=NONE --abi-check --enable-debug -C ${PREFIX}", "text/plain"),
81 ("talloc-make", "cd lib/talloc && make", "text/plain"),
82 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
84 ("tdb-configure", "cd lib/tdb && PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig ./configure --bundled-libraries=NONE --abi-check --enable-debug -C ${PREFIX}", "text/plain"),
85 ("tdb-make", "cd lib/tdb && make", "text/plain"),
86 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
88 ("tevent-configure", "cd lib/tevent && PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig ./configure --bundled-libraries=NONE --abi-check --enable-debug -C ${PREFIX}", "text/plain"),
89 ("tevent-make", "cd lib/tevent && make", "text/plain"),
90 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
92 ("ldb-configure", "cd lib/ldb && PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig ./configure --bundled-libraries=NONE --abi-check --enable-debug -C ${PREFIX}", "text/plain"),
93 ("ldb-make", "cd lib/ldb && make", "text/plain"),
94 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
96 ("configure", "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig ./configure --bundled-libraries=!talloc,!tdb,!pytdb,!ldb,!pyldb,!tevent,!pytevent --abi-check --enable-debug -C ${PREFIX}", "text/plain"),
97 ("make", "make", "text/plain"),
98 ("install", "make install", "text/plain"),
99 ("dist", "make dist", "text/plain")],
102 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
103 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
104 ("make", "make", "text/plain"),
105 ("install", "make install", "text/plain"),
106 ("test", "make test", "text/plain"),
107 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
108 ("distcheck", "make distcheck", "text/plain"),
109 ("clean", "make clean", "text/plain") ],
112 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
113 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
114 ("make", "make", "text/plain"),
115 ("install", "make install", "text/plain"),
116 ("test", "make test", "text/plain"),
117 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
118 ("distcheck", "make distcheck", "text/plain"),
119 ("clean", "make clean", "text/plain") ],
122 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
123 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
124 ("make", "make", "text/plain"),
125 ("install", "make install", "text/plain"),
126 ("test", "make test", "text/plain"),
127 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
128 ("distcheck", "make distcheck", "text/plain"),
129 ("clean", "make clean", "text/plain") ],
132 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
133 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
134 ("make", "make", "text/plain"),
135 ("install", "make install", "text/plain"),
136 ("test", "make test", "text/plain"),
137 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
138 ("distcheck", "make distcheck", "text/plain"),
139 ("clean", "make clean", "text/plain") ],
142 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
143 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
144 ("make", "make", "text/plain"),
145 ("install", "make install", "text/plain"),
146 ("test", "make test", "text/plain"),
147 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
148 ("distcheck", "make distcheck", "text/plain"),
149 ("clean", "make clean", "text/plain") ],
152 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
153 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
154 ("touch", "touch *.yp", "text/plain"),
155 ("make", "make", "text/plain"),
156 ("test", "make test", "text/plain"),
157 ("install", "make install", "text/plain"),
158 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
159 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
160 ("clean", "make clean", "text/plain") ],
162 # these are useful for debugging autobuild
163 'pass' : [ ("pass", 'echo passing && /bin/true', "text/plain") ],
164 'fail' : [ ("fail", 'echo failing && /bin/false', "text/plain") ]
167 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
169 show
= options
.verbose
171 print("Running: '%s' in '%s'" % (cmd
, dir))
173 return Popen([cmd
], shell
=True, stdout
=PIPE
, cwd
=dir).communicate()[0]
175 return check_call(cmd
, shell
=True, cwd
=dir)
177 return call(cmd
, shell
=True, cwd
=dir)
180 class builder(object):
181 '''handle build of one directory'''
183 def __init__(self
, name
, sequence
):
185 self
.dir = builddirs
[name
]
187 self
.tag
= self
.name
.replace('/', '_')
188 self
.sequence
= sequence
190 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
191 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
193 print("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
194 print("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
195 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
196 self
.stdout
= open(self
.stdout_path
, 'w')
197 self
.stderr
= open(self
.stderr_path
, 'w')
198 self
.stdin
= open("/dev/null", 'r')
199 self
.sdir
= "%s/%s" % (testbase
, self
.tag
)
200 self
.prefix
= "%s/prefix/%s" % (testbase
, self
.tag
)
201 run_cmd("rm -rf %s" % self
.sdir
)
202 cleanup_list
.append(self
.sdir
)
203 cleanup_list
.append(self
.prefix
)
204 os
.makedirs(self
.sdir
)
205 run_cmd("rm -rf %s" % self
.sdir
)
206 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
209 def start_next(self
):
210 if self
.next
== len(self
.sequence
):
211 print '%s: Completed OK' % self
.name
214 (self
.stage
, self
.cmd
, self
.output_mime_type
) = self
.sequence
[self
.next
]
215 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib
=1, prefix
=self
.prefix
))
216 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
217 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
218 # if self.output_mime_type == "text/x-subunit":
219 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
220 print '%s: [%s] Running %s' % (self
.name
, self
.stage
, self
.cmd
)
222 os
.chdir("%s/%s" % (self
.sdir
, self
.dir))
223 self
.proc
= Popen(self
.cmd
, shell
=True,
224 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
229 class buildlist(object):
230 '''handle build of multiple directories'''
232 def __init__(self
, tasklist
, tasknames
, rebase_url
, rebase_branch
="master"):
235 self
.tail_proc
= None
238 tasknames
= defaulttasks
240 # If we are only running one test,
241 # do not sleep randomly to wait for it to start
242 os
.environ
['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
245 b
= builder(n
, tasks
[n
])
248 rebase_remote
= "rebaseon"
249 retry_task
= [ ("retry",
251 git remote add -t %s %s %s
255 git describe %s/%s > old_remote_branch.desc
257 git describe %s/%s > remote_branch.desc
258 diff old_remote_branch.desc remote_branch.desc
261 rebase_branch
, rebase_remote
, rebase_url
,
263 rebase_remote
, rebase_branch
,
265 rebase_remote
, rebase_branch
269 self
.retry
= builder('retry', retry_task
)
270 self
.need_retry
= False
273 if self
.tail_proc
is not None:
274 self
.tail_proc
.terminate()
275 self
.tail_proc
.wait()
276 self
.tail_proc
= None
277 if self
.retry
is not None:
278 self
.retry
.proc
.terminate()
279 self
.retry
.proc
.wait()
282 if b
.proc
is not None:
283 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.sdir
, checkfail
=False)
295 b
.status
= b
.proc
.poll()
301 ret
= self
.retry
.proc
.poll()
303 self
.need_retry
= True
313 if options
.retry
and self
.need_retry
:
315 print("retry needed")
316 return (0, None, None, None, "retry")
319 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
321 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
324 return (0, None, None, None, "All OK")
326 def tarlogs(self
, fname
):
327 tar
= tarfile
.open(fname
, "w:gz")
329 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
330 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
331 if os
.path
.exists("autobuild.log"):
332 tar
.add("autobuild.log")
335 def remove_logs(self
):
337 os
.unlink(b
.stdout_path
)
338 os
.unlink(b
.stderr_path
)
340 def start_tail(self
):
342 cmd
= "tail -f *.stdout *.stderr"
344 self
.tail_proc
= Popen(cmd
, shell
=True)
349 if options
.nocleanup
:
351 print("Cleaning up ....")
352 for d
in cleanup_list
:
353 run_cmd("rm -rf %s" % d
)
357 '''get to the top of the git repo'''
360 if os
.path
.isdir(os
.path
.join(p
, ".git")):
362 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
366 def daemonize(logfile
):
368 if pid
== 0: # Parent
371 if pid
!= 0: # Actual daemon
376 import resource
# Resource usage information.
377 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
378 if maxfd
== resource
.RLIM_INFINITY
:
379 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
380 for fd
in range(0, maxfd
):
385 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
389 def write_pidfile(fname
):
390 '''write a pid file, cleanup on exit'''
391 f
= open(fname
, mode
='w')
392 f
.write("%u\n" % os
.getpid())
396 def rebase_tree(rebase_url
, rebase_branch
= "master"):
397 rebase_remote
= "rebaseon"
398 print("Rebasing on %s" % rebase_url
)
399 run_cmd("git describe HEAD", show
=True, dir=test_master
)
400 run_cmd("git remote add -t %s %s %s" %
401 (rebase_branch
, rebase_remote
, rebase_url
),
402 show
=True, dir=test_master
)
403 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
404 if options
.fix_whitespace
:
405 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
406 (rebase_remote
, rebase_branch
),
407 show
=True, dir=test_master
)
409 run_cmd("git rebase --force-rebase %s/%s" %
410 (rebase_remote
, rebase_branch
),
411 show
=True, dir=test_master
)
412 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
413 (rebase_remote
, rebase_branch
),
414 dir=test_master
, output
=True)
416 print("No differences between HEAD and %s/%s - exiting" %
417 (rebase_remote
, rebase_branch
))
419 run_cmd("git describe %s/%s" %
420 (rebase_remote
, rebase_branch
),
421 show
=True, dir=test_master
)
422 run_cmd("git describe HEAD", show
=True, dir=test_master
)
423 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
424 (rebase_remote
, rebase_branch
),
425 show
=True, dir=test_master
)
427 def push_to(push_url
, push_branch
= "master"):
428 push_remote
= "pushto"
429 print("Pushing to %s" % push_url
)
431 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
432 run_cmd("git commit --amend -c HEAD", dir=test_master
)
433 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
434 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
435 run_cmd("git remote add -t %s %s %s" %
436 (push_branch
, push_remote
, push_url
),
437 show
=True, dir=test_master
)
438 run_cmd("git push %s +HEAD:%s" %
439 (push_remote
, push_branch
),
440 show
=True, dir=test_master
)
442 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
444 gitroot
= find_git_root()
446 raise Exception("Failed to find git root")
448 parser
= OptionParser()
449 parser
.add_option("", "--tail", help="show output while running", default
=False, action
="store_true")
450 parser
.add_option("", "--keeplogs", help="keep logs", default
=False, action
="store_true")
451 parser
.add_option("", "--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
452 parser
.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase
,
453 default
=def_testbase
)
454 parser
.add_option("", "--passcmd", help="command to run on success", default
=None)
455 parser
.add_option("", "--verbose", help="show all commands as they are run",
456 default
=False, action
="store_true")
457 parser
.add_option("", "--rebase", help="rebase on the given tree before testing",
458 default
=None, type='str')
459 parser
.add_option("", "--pushto", help="push to a git url on success",
460 default
=None, type='str')
461 parser
.add_option("", "--mark", help="add a Tested-By signoff before pushing",
462 default
=False, action
="store_true")
463 parser
.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
464 default
=False, action
="store_true")
465 parser
.add_option("", "--retry", help="automatically retry if master changes",
466 default
=False, action
="store_true")
467 parser
.add_option("", "--email", help="send email to the given address on failure",
468 type='str', default
=None)
469 parser
.add_option("", "--email-from", help="send email from the given address",
470 type='str', default
="autobuild@samba.org")
471 parser
.add_option("", "--email-server", help="send email via the given server",
472 type='str', default
='localhost')
473 parser
.add_option("", "--always-email", help="always send email, even on success",
475 parser
.add_option("", "--daemon", help="daemonize after initial setup",
477 parser
.add_option("", "--branch", help="the branch to work on (default=master)",
478 default
="master", type='str')
479 parser
.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
480 default
=gitroot
, type='str')
481 parser
.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
482 default
=False, action
="store_true")
484 def send_email(subject
, text
, log_tar
):
485 outer
= MIMEMultipart()
486 outer
['Subject'] = subject
487 outer
['To'] = options
.email
488 outer
['From'] = options
.email_from
489 outer
['Date'] = email
.utils
.formatdate(localtime
= True)
490 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
491 outer
.attach(MIMEText(text
, 'plain'))
492 if options
.attach_logs
:
493 fp
= open(log_tar
, 'rb')
494 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
496 # Set the filename parameter
497 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
499 content
= outer
.as_string()
500 s
= smtplib
.SMTP(options
.email_server
)
501 s
.sendmail(options
.email_from
, [options
.email
], content
)
505 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
, log_base
=None):
506 '''send an email to options.email about the failure'''
507 user
= os
.getenv("USER")
513 Your autobuild on %s failed when trying to test %s with the following error:
516 the autobuild has been abandoned. Please fix the error and resubmit.
518 A summary of the autobuild process is here:
521 ''' % (platform
.node(), failed_task
, errstr
, log_base
)
523 if failed_task
!= 'rebase':
525 You can see logs of the failed task here:
530 or you can get full logs of all tasks in this job here:
534 The top commit for the tree that was built was:
538 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
540 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
541 send_email('autobuild failure on %s for task %s during %s'
542 % (platform
.node(), failed_task
, failed_stage
),
545 def email_success(log_base
=None):
546 '''send an email to options.email about a successful build'''
547 user
= os
.getenv("USER")
553 Your autobuild on %s has succeeded.
555 ''' % platform
.node()
560 you can get full logs of all tasks in this job here:
567 The top commit for the tree that was built was:
572 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
573 send_email('autobuild sucess on %s ' % platform
.node(),
577 (options
, args
) = parser
.parse_args()
580 if options
.rebase
is None:
581 raise Exception('You can only use --retry if you also rebase')
583 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
584 test_master
= "%s/master" % testbase
586 # get the top commit message, for emails
587 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
590 os
.makedirs(testbase
)
591 except Exception, reason
:
592 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
593 cleanup_list
.append(testbase
)
596 logfile
= os
.path
.join(testbase
, "log")
597 print "Forking into the background, writing progress to %s" % logfile
600 write_pidfile(gitroot
+ "/autobuild.pid")
604 run_cmd("rm -rf %s" % test_master
)
605 cleanup_list
.append(test_master
)
606 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
613 if options
.rebase
is not None:
614 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
616 cleanup_list
.append(gitroot
+ "/autobuild.pid")
618 email_failure(-1, 'rebase', 'rebase', 'rebase',
619 'rebase on %s failed' % options
.branch
,
620 log_base
=options
.log_base
)
622 blist
= buildlist(tasks
, args
, options
.rebase
, rebase_branch
=options
.branch
)
625 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
626 if status
!= 0 or errstr
!= "retry":
633 cleanup_list
.append(gitroot
+ "/autobuild.pid")
637 print("waiting for tail to flush")
642 if options
.passcmd
is not None:
643 print("Running passcmd: %s" % options
.passcmd
)
644 run_cmd(options
.passcmd
, dir=test_master
)
645 if options
.pushto
is not None:
646 push_to(options
.pushto
, push_branch
=options
.branch
)
647 if options
.keeplogs
or options
.attach_logs
:
648 blist
.tarlogs("logs.tar.gz")
649 print("Logs in logs.tar.gz")
650 if options
.always_email
:
651 email_success(log_base
=options
.log_base
)
657 # something failed, gather a tar of the logs
658 blist
.tarlogs("logs.tar.gz")
660 if options
.email
is not None:
661 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
, log_base
=options
.log_base
)
665 print("Logs in logs.tar.gz")