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'
31 "talloc" : "lib/talloc",
32 "replace" : "lib/replace",
33 "tevent" : "lib/tevent",
40 defaulttasks
= [ "ctdb", "samba", "samba-xc", "samba-ctdb", "samba-libs", "ldb", "tdb", "talloc", "replace", "tevent", "pidl" ]
42 samba_configure_params
= " --picky-developer ${PREFIX} --with-profiling-data"
44 samba_libs_envvars
= "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH"
45 samba_libs_envvars
+= " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
46 samba_libs_envvars
+= " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
47 samba_libs_configure_base
= samba_libs_envvars
+ " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX}"
48 samba_libs_configure_libs
= samba_libs_configure_base
+ " --bundled-libraries=NONE"
49 samba_libs_configure_samba
= samba_libs_configure_base
+ " --bundled-libraries=!talloc,!tdb,!pytdb,!ldb,!pyldb,!tevent,!pytevent"
52 "ctdb" : [ ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
53 ("configure", "./configure ${PREFIX}", "text/plain"),
54 ("make", "make all", "text/plain"),
55 ("install", "make install", "text/plain"),
56 ("test", "make autotest", "text/plain"),
57 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
58 ("clean", "make clean", "text/plain") ],
60 # We have 'test' before 'install' because, 'test' should work without 'install'
61 "samba" : [ ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
62 ("make", "make -j", "text/plain"),
63 ("test", "make test FAIL_IMMEDIATELY=1", "text/plain"),
64 ("install", "make install", "text/plain"),
65 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
66 ("clean", "make clean", "text/plain") ],
68 # Test cross-compile infrastructure
69 "samba-xc" : [ ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
70 ("configure-cross-execute", "./configure.developer -b ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
71 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params
, "text/plain"),
72 ("configure-cross-answers", "./configure.developer -b ./bin-xa --cross-compile" \
73 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params
, "text/plain"),
74 ("compare-results", "script/compare_cc_results.py ./bin/c4che/default.cache.py ./bin-xe/c4che/default.cache.py ./bin-xa/c4che/default.cache.py", "text/plain")],
77 "samba-ctdb" : [ ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
79 # make sure we have tdb around:
80 ("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"),
81 ("tdb-make", "cd lib/tdb && make", "text/plain"),
82 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
85 # build samba with cluster support (also building ctdb):
86 ("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"),
87 ("samba-make", "make", "text/plain"),
88 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
89 ("samba-install", "make install", "text/plain"),
90 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
93 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
94 ("clean", "make clean", "text/plain"),
95 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain") ],
98 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
99 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs
, "text/plain"),
100 ("talloc-make", "cd lib/talloc && make", "text/plain"),
101 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
103 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs
, "text/plain"),
104 ("tdb-make", "cd lib/tdb && make", "text/plain"),
105 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
107 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs
, "text/plain"),
108 ("tevent-make", "cd lib/tevent && make", "text/plain"),
109 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
111 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs
, "text/plain"),
112 ("ldb-make", "cd lib/ldb && make", "text/plain"),
113 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
115 ("configure", samba_libs_configure_samba
, "text/plain"),
116 ("make", "make", "text/plain"),
117 ("install", "make install", "text/plain"),
118 ("dist", "make dist", "text/plain"),
120 # retry with all modules shared
121 ("allshared-distclean", "make distclean", "text/plain"),
122 ("allshared-configure", samba_libs_configure_samba
+ " --with-shared-modules=ALL", "text/plain"),
123 ("allshared-make", "make", "text/plain"),
125 # retry with all modules static
126 ("allstatic-distclean", "make distclean", "text/plain"),
127 ("allstatic-configure", samba_libs_configure_samba
+ " --with-static-modules=ALL", "text/plain"),
128 ("allstatic-make", "make", "text/plain"),
130 # retry without any required modules
131 ("none-distclean", "make distclean", "text/plain"),
132 ("none-configure", samba_libs_configure_samba
+ " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
133 ("none-make", "make", "text/plain"),
135 # retry with nonshared smbd and smbtorture
136 ("nonshared-distclean", "make distclean", "text/plain"),
137 ("nonshared-configure", samba_libs_configure_base
+ " --bundled-libraries=talloc,tdb,pytdb,ldb,pyldb,tevent,pytevent --with-static-modules=ALL --nonshared-binary=smbtorture,smbd/smbd", "text/plain"),
138 ("nonshared-make", "make", "text/plain")],
141 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
142 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
143 ("make", "make", "text/plain"),
144 ("install", "make install", "text/plain"),
145 ("test", "make test", "text/plain"),
146 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
147 ("distcheck", "make distcheck", "text/plain"),
148 ("clean", "make clean", "text/plain") ],
151 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
152 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
153 ("make", "make", "text/plain"),
154 ("install", "make install", "text/plain"),
155 ("test", "make test", "text/plain"),
156 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
157 ("distcheck", "make distcheck", "text/plain"),
158 ("clean", "make clean", "text/plain") ],
161 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
162 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
163 ("make", "make", "text/plain"),
164 ("install", "make install", "text/plain"),
165 ("test", "make test", "text/plain"),
166 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
167 ("distcheck", "make distcheck", "text/plain"),
168 ("clean", "make clean", "text/plain") ],
171 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
172 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
173 ("make", "make", "text/plain"),
174 ("install", "make install", "text/plain"),
175 ("test", "make test", "text/plain"),
176 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
177 ("distcheck", "make distcheck", "text/plain"),
178 ("clean", "make clean", "text/plain") ],
181 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
182 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
183 ("make", "make", "text/plain"),
184 ("install", "make install", "text/plain"),
185 ("test", "make test", "text/plain"),
186 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
187 ("distcheck", "make distcheck", "text/plain"),
188 ("clean", "make clean", "text/plain") ],
191 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
192 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
193 ("touch", "touch *.yp", "text/plain"),
194 ("make", "make", "text/plain"),
195 ("test", "make test", "text/plain"),
196 ("install", "make install", "text/plain"),
197 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
198 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
199 ("clean", "make clean", "text/plain") ],
201 # these are useful for debugging autobuild
202 'pass' : [ ("pass", 'echo passing && /bin/true', "text/plain") ],
203 'fail' : [ ("fail", 'echo failing && /bin/false', "text/plain") ]
206 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
208 show
= options
.verbose
210 print("Running: '%s' in '%s'" % (cmd
, dir))
212 return Popen([cmd
], shell
=True, stdout
=PIPE
, cwd
=dir).communicate()[0]
214 return check_call(cmd
, shell
=True, cwd
=dir)
216 return call(cmd
, shell
=True, cwd
=dir)
219 class builder(object):
220 '''handle build of one directory'''
222 def __init__(self
, name
, sequence
):
224 self
.dir = builddirs
[name
]
226 self
.tag
= self
.name
.replace('/', '_')
227 self
.sequence
= sequence
229 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
230 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
232 print("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
233 print("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
234 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
235 self
.stdout
= open(self
.stdout_path
, 'w')
236 self
.stderr
= open(self
.stderr_path
, 'w')
237 self
.stdin
= open("/dev/null", 'r')
238 self
.sdir
= "%s/%s" % (testbase
, self
.tag
)
239 self
.prefix
= "%s/prefix/%s" % (testbase
, self
.tag
)
240 run_cmd("rm -rf %s" % self
.sdir
)
241 cleanup_list
.append(self
.sdir
)
242 cleanup_list
.append(self
.prefix
)
243 os
.makedirs(self
.sdir
)
244 run_cmd("rm -rf %s" % self
.sdir
)
245 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
248 def start_next(self
):
249 if self
.next
== len(self
.sequence
):
250 print '%s: Completed OK' % self
.name
253 (self
.stage
, self
.cmd
, self
.output_mime_type
) = self
.sequence
[self
.next
]
254 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib
=1, prefix
=self
.prefix
))
255 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
256 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
257 # if self.output_mime_type == "text/x-subunit":
258 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
259 print '%s: [%s] Running %s' % (self
.name
, self
.stage
, self
.cmd
)
261 os
.chdir("%s/%s" % (self
.sdir
, self
.dir))
262 self
.proc
= Popen(self
.cmd
, shell
=True,
263 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
268 class buildlist(object):
269 '''handle build of multiple directories'''
271 def __init__(self
, tasklist
, tasknames
, rebase_url
, rebase_branch
="master"):
274 self
.tail_proc
= None
277 tasknames
= defaulttasks
279 # If we are only running one test,
280 # do not sleep randomly to wait for it to start
281 os
.environ
['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
284 b
= builder(n
, tasks
[n
])
287 rebase_remote
= "rebaseon"
288 retry_task
= [ ("retry",
290 git remote add -t %s %s %s
294 git describe %s/%s > old_remote_branch.desc
296 git describe %s/%s > remote_branch.desc
297 diff old_remote_branch.desc remote_branch.desc
300 rebase_branch
, rebase_remote
, rebase_url
,
302 rebase_remote
, rebase_branch
,
304 rebase_remote
, rebase_branch
308 self
.retry
= builder('retry', retry_task
)
309 self
.need_retry
= False
312 if self
.tail_proc
is not None:
313 self
.tail_proc
.terminate()
314 self
.tail_proc
.wait()
315 self
.tail_proc
= None
316 if self
.retry
is not None:
317 self
.retry
.proc
.terminate()
318 self
.retry
.proc
.wait()
321 if b
.proc
is not None:
322 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.sdir
, checkfail
=False)
334 b
.status
= b
.proc
.poll()
340 ret
= self
.retry
.proc
.poll()
342 self
.need_retry
= True
352 if options
.retry
and self
.need_retry
:
354 print("retry needed")
355 return (0, None, None, None, "retry")
358 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
360 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
363 return (0, None, None, None, "All OK")
365 def write_system_info(self
):
366 filename
= 'system-info.txt'
367 f
= open(filename
, 'w')
368 for cmd
in ['uname -a', 'free', 'cat /proc/cpuinfo']:
369 print >>f
, '### %s' % cmd
370 print >>f
, run_cmd(cmd
, output
=True, checkfail
=False)
375 def tarlogs(self
, fname
):
376 tar
= tarfile
.open(fname
, "w:gz")
378 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
379 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
380 if os
.path
.exists("autobuild.log"):
381 tar
.add("autobuild.log")
382 sys_info
= self
.write_system_info()
386 def remove_logs(self
):
388 os
.unlink(b
.stdout_path
)
389 os
.unlink(b
.stderr_path
)
391 def start_tail(self
):
393 cmd
= "tail -f *.stdout *.stderr"
395 self
.tail_proc
= Popen(cmd
, shell
=True)
400 if options
.nocleanup
:
402 print("Cleaning up ....")
403 for d
in cleanup_list
:
404 run_cmd("rm -rf %s" % d
)
408 '''get to the top of the git repo'''
411 if os
.path
.isdir(os
.path
.join(p
, ".git")):
413 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
417 def daemonize(logfile
):
419 if pid
== 0: # Parent
422 if pid
!= 0: # Actual daemon
427 import resource
# Resource usage information.
428 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
429 if maxfd
== resource
.RLIM_INFINITY
:
430 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
431 for fd
in range(0, maxfd
):
436 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
440 def write_pidfile(fname
):
441 '''write a pid file, cleanup on exit'''
442 f
= open(fname
, mode
='w')
443 f
.write("%u\n" % os
.getpid())
447 def rebase_tree(rebase_url
, rebase_branch
= "master"):
448 rebase_remote
= "rebaseon"
449 print("Rebasing on %s" % rebase_url
)
450 run_cmd("git describe HEAD", show
=True, dir=test_master
)
451 run_cmd("git remote add -t %s %s %s" %
452 (rebase_branch
, rebase_remote
, rebase_url
),
453 show
=True, dir=test_master
)
454 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
455 if options
.fix_whitespace
:
456 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
457 (rebase_remote
, rebase_branch
),
458 show
=True, dir=test_master
)
460 run_cmd("git rebase --force-rebase %s/%s" %
461 (rebase_remote
, rebase_branch
),
462 show
=True, dir=test_master
)
463 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
464 (rebase_remote
, rebase_branch
),
465 dir=test_master
, output
=True)
467 print("No differences between HEAD and %s/%s - exiting" %
468 (rebase_remote
, rebase_branch
))
470 run_cmd("git describe %s/%s" %
471 (rebase_remote
, rebase_branch
),
472 show
=True, dir=test_master
)
473 run_cmd("git describe HEAD", show
=True, dir=test_master
)
474 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
475 (rebase_remote
, rebase_branch
),
476 show
=True, dir=test_master
)
478 def push_to(push_url
, push_branch
= "master"):
479 push_remote
= "pushto"
480 print("Pushing to %s" % push_url
)
482 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
483 run_cmd("git commit --amend -c HEAD", dir=test_master
)
484 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
485 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
486 run_cmd("git remote add -t %s %s %s" %
487 (push_branch
, push_remote
, push_url
),
488 show
=True, dir=test_master
)
489 run_cmd("git push %s +HEAD:%s" %
490 (push_remote
, push_branch
),
491 show
=True, dir=test_master
)
493 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
495 gitroot
= find_git_root()
497 raise Exception("Failed to find git root")
499 parser
= OptionParser()
500 parser
.add_option("", "--tail", help="show output while running", default
=False, action
="store_true")
501 parser
.add_option("", "--keeplogs", help="keep logs", default
=False, action
="store_true")
502 parser
.add_option("", "--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
503 parser
.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase
,
504 default
=def_testbase
)
505 parser
.add_option("", "--passcmd", help="command to run on success", default
=None)
506 parser
.add_option("", "--verbose", help="show all commands as they are run",
507 default
=False, action
="store_true")
508 parser
.add_option("", "--rebase", help="rebase on the given tree before testing",
509 default
=None, type='str')
510 parser
.add_option("", "--pushto", help="push to a git url on success",
511 default
=None, type='str')
512 parser
.add_option("", "--mark", help="add a Tested-By signoff before pushing",
513 default
=False, action
="store_true")
514 parser
.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
515 default
=False, action
="store_true")
516 parser
.add_option("", "--retry", help="automatically retry if master changes",
517 default
=False, action
="store_true")
518 parser
.add_option("", "--email", help="send email to the given address on failure",
519 type='str', default
=None)
520 parser
.add_option("", "--email-from", help="send email from the given address",
521 type='str', default
="autobuild@samba.org")
522 parser
.add_option("", "--email-server", help="send email via the given server",
523 type='str', default
='localhost')
524 parser
.add_option("", "--always-email", help="always send email, even on success",
526 parser
.add_option("", "--daemon", help="daemonize after initial setup",
528 parser
.add_option("", "--branch", help="the branch to work on (default=master)",
529 default
="master", type='str')
530 parser
.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
531 default
=gitroot
, type='str')
532 parser
.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
533 default
=False, action
="store_true")
535 def send_email(subject
, text
, log_tar
):
536 outer
= MIMEMultipart()
537 outer
['Subject'] = subject
538 outer
['To'] = options
.email
539 outer
['From'] = options
.email_from
540 outer
['Date'] = email
.utils
.formatdate(localtime
= True)
541 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
542 outer
.attach(MIMEText(text
, 'plain'))
543 if options
.attach_logs
:
544 fp
= open(log_tar
, 'rb')
545 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
547 # Set the filename parameter
548 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
550 content
= outer
.as_string()
551 s
= smtplib
.SMTP(options
.email_server
)
552 s
.sendmail(options
.email_from
, [options
.email
], content
)
556 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
557 elapsed_time
, log_base
=None):
558 '''send an email to options.email about the failure'''
559 elapsed_minutes
= elapsed_time
/ 60.0
560 user
= os
.getenv("USER")
566 Your autobuild on %s failed after %.1f minutes
567 when trying to test %s with the following error:
571 the autobuild has been abandoned. Please fix the error and resubmit.
573 A summary of the autobuild process is here:
576 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
578 if failed_task
!= 'rebase':
580 You can see logs of the failed task here:
585 or you can get full logs of all tasks in this job here:
589 The top commit for the tree that was built was:
593 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
595 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
596 send_email('autobuild failure on %s for task %s during %s'
597 % (platform
.node(), failed_task
, failed_stage
),
600 def email_success(elapsed_time
, log_base
=None):
601 '''send an email to options.email about a successful build'''
602 user
= os
.getenv("USER")
608 Your autobuild on %s has succeeded after %.1f minutes.
610 ''' % (platform
.node(), elapsed_time
/ 60.)
615 you can get full logs of all tasks in this job here:
622 The top commit for the tree that was built was:
627 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
628 send_email('autobuild sucess on %s ' % platform
.node(),
632 (options
, args
) = parser
.parse_args()
635 if options
.rebase
is None:
636 raise Exception('You can only use --retry if you also rebase')
638 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
639 test_master
= "%s/master" % testbase
641 # get the top commit message, for emails
642 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
645 os
.makedirs(testbase
)
646 except Exception, reason
:
647 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
648 cleanup_list
.append(testbase
)
651 logfile
= os
.path
.join(testbase
, "log")
652 print "Forking into the background, writing progress to %s" % logfile
655 write_pidfile(gitroot
+ "/autobuild.pid")
657 start_time
= time
.time()
661 run_cmd("rm -rf %s" % test_master
)
662 cleanup_list
.append(test_master
)
663 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
670 if options
.rebase
is not None:
671 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
673 cleanup_list
.append(gitroot
+ "/autobuild.pid")
675 elapsed_time
= time
.time() - start_time
676 email_failure(-1, 'rebase', 'rebase', 'rebase',
677 'rebase on %s failed' % options
.branch
,
678 elapsed_time
, log_base
=options
.log_base
)
680 blist
= buildlist(tasks
, args
, options
.rebase
, rebase_branch
=options
.branch
)
683 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
684 if status
!= 0 or errstr
!= "retry":
691 cleanup_list
.append(gitroot
+ "/autobuild.pid")
695 print("waiting for tail to flush")
698 elapsed_time
= time
.time() - start_time
701 if options
.passcmd
is not None:
702 print("Running passcmd: %s" % options
.passcmd
)
703 run_cmd(options
.passcmd
, dir=test_master
)
704 if options
.pushto
is not None:
705 push_to(options
.pushto
, push_branch
=options
.branch
)
706 if options
.keeplogs
or options
.attach_logs
:
707 blist
.tarlogs("logs.tar.gz")
708 print("Logs in logs.tar.gz")
709 if options
.always_email
:
710 email_success(elapsed_time
, log_base
=options
.log_base
)
716 # something failed, gather a tar of the logs
717 blist
.tarlogs("logs.tar.gz")
719 if options
.email
is not None:
720 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
721 elapsed_time
, log_base
=options
.log_base
)
725 print("Logs in logs.tar.gz")