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 os
.environ
["PYTHONUNBUFFERED"] = "1"
20 # This speeds up testing remarkably.
21 os
.environ
['TDB_NO_FSYNC'] = '1'
33 "samba-test-only" : ".",
34 "samba-systemkrb5" : ".",
37 "talloc" : "lib/talloc",
38 "replace" : "lib/replace",
39 "tevent" : "lib/tevent",
46 defaulttasks
= [ "ctdb", "samba", "samba-xc", "samba-o3", "samba-ctdb", "samba-libs", "samba-static", "samba-systemkrb5", "ldb", "tdb", "talloc", "replace", "tevent", "pidl" ]
48 if os
.environ
.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
49 defaulttasks
.remove("samba-o3")
51 ctdb_configure_params
= " --enable-developer --picky-developer ${PREFIX}"
52 samba_configure_params
= " --picky-developer ${PREFIX} ${EXTRA_PYTHON} --with-profiling-data"
54 samba_libs_envvars
= "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH"
55 samba_libs_envvars
+= " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
56 samba_libs_envvars
+= " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
57 samba_libs_configure_base
= samba_libs_envvars
+ " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX} ${EXTRA_PYTHON}"
58 samba_libs_configure_libs
= samba_libs_configure_base
+ " --bundled-libraries=NONE"
59 samba_libs_configure_samba
= samba_libs_configure_base
+ " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent"
61 if os
.environ
.get("AUTOBUILD_NO_EXTRA_PYTHON", "0") == "1":
64 extra_python
= "--extra-python=/usr/bin/python3"
67 "ctdb" : [ ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
68 ("configure", "./configure " + ctdb_configure_params
, "text/plain"),
69 ("make", "make all", "text/plain"),
70 ("install", "make install", "text/plain"),
71 ("test", "make autotest", "text/plain"),
72 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
73 ("clean", "make clean", "text/plain") ],
75 # We have 'test' before 'install' because, 'test' should work without 'install'
76 "samba" : [ ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
77 ("make", "make -j", "text/plain"),
78 ("test", "make test FAIL_IMMEDIATELY=1", "text/plain"),
79 ("install", "make install", "text/plain"),
80 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
81 ("clean", "make clean", "text/plain") ],
83 "samba-test-only" : [ ("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params
, "text/plain"),
84 ("make", "make -j", "text/plain"),
85 ("test", "make test FAIL_IMMEDIATELY=1 TESTS=${TESTS}", "text/plain") ],
87 # Test cross-compile infrastructure
88 "samba-xc" : [ ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
89 ("configure-cross-execute", "./configure.developer -b ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
90 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params
, "text/plain"),
91 ("configure-cross-answers", "./configure.developer -b ./bin-xa --cross-compile" \
92 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params
, "text/plain"),
93 ("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")],
95 # test build with -O3 -- catches extra warnings and bugs
96 "samba-o3" : [ ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
97 ("configure", "ADDITIONAL_CFLAGS='-O3' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params
, "text/plain"),
98 ("make", "make -j", "text/plain"),
99 ("test", "make quicktest FAIL_IMMEDIATELY=1 TESTS='\(ad_dc\)'", "text/plain"),
100 ("install", "make install", "text/plain"),
101 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
102 ("clean", "make clean", "text/plain") ],
104 "samba-ctdb" : [ ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
106 # make sure we have tdb around:
107 ("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"),
108 ("tdb-make", "cd lib/tdb && make", "text/plain"),
109 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
112 # build samba with cluster support (also building ctdb):
113 ("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"),
114 ("samba-make", "make", "text/plain"),
115 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
116 ("samba-install", "make install", "text/plain"),
117 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
120 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
121 ("clean", "make clean", "text/plain"),
122 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain") ],
125 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
126 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs
, "text/plain"),
127 ("talloc-make", "cd lib/talloc && make", "text/plain"),
128 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
130 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs
, "text/plain"),
131 ("tdb-make", "cd lib/tdb && make", "text/plain"),
132 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
134 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs
, "text/plain"),
135 ("tevent-make", "cd lib/tevent && make", "text/plain"),
136 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
138 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs
, "text/plain"),
139 ("ldb-make", "cd lib/ldb && make", "text/plain"),
140 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
142 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
143 ("nondevel-make", "make -j", "text/plain"),
144 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
145 ("nondevel-install", "make install", "text/plain"),
146 ("nondevel-dist", "make dist", "text/plain"),
148 # retry with all modules shared
149 ("allshared-distclean", "make distclean", "text/plain"),
150 ("allshared-configure", samba_libs_configure_samba
+ " --with-shared-modules=ALL", "text/plain"),
151 ("allshared-make", "make -j", "text/plain")],
154 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
155 # build with all modules static
156 ("allstatic-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=ALL", "text/plain"),
157 ("allstatic-make", "make -j", "text/plain"),
159 # retry without any required modules
160 ("none-distclean", "make distclean", "text/plain"),
161 ("none-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
162 ("none-make", "make -j", "text/plain"),
164 # retry with nonshared smbd and smbtorture
165 ("nonshared-distclean", "make distclean", "text/plain"),
166 ("nonshared-configure", "./configure.developer " + samba_configure_params
+ " --bundled-libraries=talloc,tdb,pytdb,ldb,pyldb,tevent,pytevent --with-static-modules=ALL --nonshared-binary=smbtorture,smbd/smbd", "text/plain"),
167 ("nonshared-make", "make -j", "text/plain")],
169 "samba-systemkrb5" : [
170 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
171 ("configure", "./configure.developer " + samba_configure_params
+ " --with-system-mitkrb5 --without-ad-dc", "text/plain"),
172 ("make", "make -j", "text/plain"),
173 # we currently cannot run a full make test, a limited list of tests could be run
174 # via "make test TESTS=sometests"
175 ("test", "make test FAIL_IMMEDIATELY=1 TESTS='samba3.*ktest'", "text/plain"),
176 ("install", "make install", "text/plain"),
177 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
178 ("clean", "make clean", "text/plain")
184 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
185 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
186 ("make", "make", "text/plain"),
187 ("install", "make install", "text/plain"),
188 ("test", "make test", "text/plain"),
189 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
190 ("distcheck", "make distcheck", "text/plain"),
191 ("clean", "make clean", "text/plain") ],
194 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
195 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
196 ("make", "make", "text/plain"),
197 ("install", "make install", "text/plain"),
198 ("test", "make test", "text/plain"),
199 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
200 ("distcheck", "make distcheck", "text/plain"),
201 ("clean", "make clean", "text/plain") ],
204 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
205 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
206 ("make", "make", "text/plain"),
207 ("install", "make install", "text/plain"),
208 ("test", "make test", "text/plain"),
209 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
210 ("distcheck", "make distcheck", "text/plain"),
211 ("clean", "make clean", "text/plain") ],
214 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
215 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
216 ("make", "make", "text/plain"),
217 ("install", "make install", "text/plain"),
218 ("test", "make test", "text/plain"),
219 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
220 ("distcheck", "make distcheck", "text/plain"),
221 ("clean", "make clean", "text/plain") ],
224 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
225 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
226 ("make", "make", "text/plain"),
227 ("install", "make install", "text/plain"),
228 ("test", "make test", "text/plain"),
229 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
230 ("distcheck", "make distcheck", "text/plain"),
231 ("clean", "make clean", "text/plain") ],
234 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
235 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
236 ("touch", "touch *.yp", "text/plain"),
237 ("make", "make", "text/plain"),
238 ("test", "make test", "text/plain"),
239 ("install", "make install", "text/plain"),
240 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
241 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
242 ("clean", "make clean", "text/plain") ],
244 # these are useful for debugging autobuild
245 'pass' : [ ("pass", 'echo passing && /bin/true', "text/plain") ],
246 'fail' : [ ("fail", 'echo failing && /bin/false', "text/plain") ]
254 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
256 show
= options
.verbose
258 do_print("Running: '%s' in '%s'" % (cmd
, dir))
260 return Popen([cmd
], shell
=True, stdout
=PIPE
, cwd
=dir).communicate()[0]
262 return check_call(cmd
, shell
=True, cwd
=dir)
264 return call(cmd
, shell
=True, cwd
=dir)
267 class builder(object):
268 '''handle build of one directory'''
270 def __init__(self
, name
, sequence
, cp
=True):
272 self
.dir = builddirs
[name
]
274 self
.tag
= self
.name
.replace('/', '_')
275 self
.sequence
= sequence
277 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
278 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
280 do_print("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
281 do_print("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
282 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
283 self
.stdout
= open(self
.stdout_path
, 'w')
284 self
.stderr
= open(self
.stderr_path
, 'w')
285 self
.stdin
= open("/dev/null", 'r')
286 self
.sdir
= "%s/%s" % (testbase
, self
.tag
)
287 self
.prefix
= "%s/%s" % (test_prefix
, self
.tag
)
288 run_cmd("rm -rf %s" % self
.sdir
)
289 run_cmd("rm -rf %s" % self
.prefix
)
291 run_cmd("cp --recursive --link --archive %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
293 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
296 def start_next(self
):
297 if self
.next
== len(self
.sequence
):
298 if not options
.nocleanup
:
299 run_cmd("rm -rf %s" % self
.sdir
)
300 run_cmd("rm -rf %s" % self
.prefix
)
301 do_print('%s: Completed OK' % self
.name
)
304 (self
.stage
, self
.cmd
, self
.output_mime_type
) = self
.sequence
[self
.next
]
305 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib
=1, prefix
=self
.prefix
))
306 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
307 self
.cmd
= self
.cmd
.replace("${EXTRA_PYTHON}", "%s" % extra_python
)
308 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
309 self
.cmd
= self
.cmd
.replace("${TESTS}", options
.restrict_tests
)
310 # if self.output_mime_type == "text/x-subunit":
311 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
312 do_print('%s: [%s] Running %s' % (self
.name
, self
.stage
, self
.cmd
))
314 os
.chdir("%s/%s" % (self
.sdir
, self
.dir))
315 self
.proc
= Popen(self
.cmd
, shell
=True,
316 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
321 class buildlist(object):
322 '''handle build of multiple directories'''
324 def __init__(self
, tasknames
, rebase_url
, rebase_branch
="master"):
327 self
.tail_proc
= None
330 if options
.restrict_tests
:
331 tasknames
= ["samba-test-only"]
333 tasknames
= defaulttasks
335 # If we are only running one test,
336 # do not sleep randomly to wait for it to start
337 os
.environ
['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
340 b
= builder(n
, tasks
[n
], cp
=n
is not "pidl")
343 rebase_remote
= "rebaseon"
344 retry_task
= [ ("retry",
346 git remote add -t %s %s %s
350 git describe %s/%s > old_remote_branch.desc
352 git describe %s/%s > remote_branch.desc
353 diff old_remote_branch.desc remote_branch.desc
356 rebase_branch
, rebase_remote
, rebase_url
,
358 rebase_remote
, rebase_branch
,
360 rebase_remote
, rebase_branch
364 self
.retry
= builder('retry', retry_task
, cp
=False)
365 self
.need_retry
= False
368 if self
.tail_proc
is not None:
369 self
.tail_proc
.terminate()
370 self
.tail_proc
.wait()
371 self
.tail_proc
= None
372 if self
.retry
is not None:
373 self
.retry
.proc
.terminate()
374 self
.retry
.proc
.wait()
377 if b
.proc
is not None:
378 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.sdir
, checkfail
=False)
390 b
.status
= b
.proc
.poll()
396 ret
= self
.retry
.proc
.poll()
398 self
.need_retry
= True
408 if options
.retry
and self
.need_retry
:
410 do_print("retry needed")
411 return (0, None, None, None, "retry")
414 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
416 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
419 return (0, None, None, None, "All OK")
421 def write_system_info(self
):
422 filename
= 'system-info.txt'
423 f
= open(filename
, 'w')
424 for cmd
in ['uname -a', 'free', 'cat /proc/cpuinfo']:
425 print >>f
, '### %s' % cmd
426 print >>f
, run_cmd(cmd
, output
=True, checkfail
=False)
431 def tarlogs(self
, fname
):
432 tar
= tarfile
.open(fname
, "w:gz")
434 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
435 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
436 if os
.path
.exists("autobuild.log"):
437 tar
.add("autobuild.log")
438 sys_info
= self
.write_system_info()
442 def remove_logs(self
):
444 os
.unlink(b
.stdout_path
)
445 os
.unlink(b
.stderr_path
)
447 def start_tail(self
):
449 cmd
= "tail -f *.stdout *.stderr"
451 self
.tail_proc
= Popen(cmd
, shell
=True)
456 if options
.nocleanup
:
458 do_print("Cleaning up ....")
459 for d
in cleanup_list
:
460 run_cmd("rm -rf %s" % d
)
464 '''get to the top of the git repo'''
467 if os
.path
.isdir(os
.path
.join(p
, ".git")):
469 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
473 def daemonize(logfile
):
475 if pid
== 0: # Parent
478 if pid
!= 0: # Actual daemon
483 import resource
# Resource usage information.
484 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
485 if maxfd
== resource
.RLIM_INFINITY
:
486 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
487 for fd
in range(0, maxfd
):
492 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
496 def write_pidfile(fname
):
497 '''write a pid file, cleanup on exit'''
498 f
= open(fname
, mode
='w')
499 f
.write("%u\n" % os
.getpid())
503 def rebase_tree(rebase_url
, rebase_branch
= "master"):
504 rebase_remote
= "rebaseon"
505 do_print("Rebasing on %s" % rebase_url
)
506 run_cmd("git describe HEAD", show
=True, dir=test_master
)
507 run_cmd("git remote add -t %s %s %s" %
508 (rebase_branch
, rebase_remote
, rebase_url
),
509 show
=True, dir=test_master
)
510 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
511 if options
.fix_whitespace
:
512 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
513 (rebase_remote
, rebase_branch
),
514 show
=True, dir=test_master
)
516 run_cmd("git rebase --force-rebase %s/%s" %
517 (rebase_remote
, rebase_branch
),
518 show
=True, dir=test_master
)
519 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
520 (rebase_remote
, rebase_branch
),
521 dir=test_master
, output
=True)
523 do_print("No differences between HEAD and %s/%s - exiting" %
524 (rebase_remote
, rebase_branch
))
526 run_cmd("git describe %s/%s" %
527 (rebase_remote
, rebase_branch
),
528 show
=True, dir=test_master
)
529 run_cmd("git describe HEAD", show
=True, dir=test_master
)
530 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
531 (rebase_remote
, rebase_branch
),
532 show
=True, dir=test_master
)
534 def push_to(push_url
, push_branch
= "master"):
535 push_remote
= "pushto"
536 do_print("Pushing to %s" % push_url
)
538 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
539 run_cmd("git commit --amend -c HEAD", dir=test_master
)
540 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
541 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
542 run_cmd("git remote add -t %s %s %s" %
543 (push_branch
, push_remote
, push_url
),
544 show
=True, dir=test_master
)
545 run_cmd("git push %s +HEAD:%s" %
546 (push_remote
, push_branch
),
547 show
=True, dir=test_master
)
549 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
551 gitroot
= find_git_root()
553 raise Exception("Failed to find git root")
555 parser
= OptionParser()
556 parser
.add_option("", "--tail", help="show output while running", default
=False, action
="store_true")
557 parser
.add_option("", "--keeplogs", help="keep logs", default
=False, action
="store_true")
558 parser
.add_option("", "--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
559 parser
.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase
,
560 default
=def_testbase
)
561 parser
.add_option("", "--passcmd", help="command to run on success", default
=None)
562 parser
.add_option("", "--verbose", help="show all commands as they are run",
563 default
=False, action
="store_true")
564 parser
.add_option("", "--rebase", help="rebase on the given tree before testing",
565 default
=None, type='str')
566 parser
.add_option("", "--pushto", help="push to a git url on success",
567 default
=None, type='str')
568 parser
.add_option("", "--mark", help="add a Tested-By signoff before pushing",
569 default
=False, action
="store_true")
570 parser
.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
571 default
=False, action
="store_true")
572 parser
.add_option("", "--retry", help="automatically retry if master changes",
573 default
=False, action
="store_true")
574 parser
.add_option("", "--email", help="send email to the given address on failure",
575 type='str', default
=None)
576 parser
.add_option("", "--email-from", help="send email from the given address",
577 type='str', default
="autobuild@samba.org")
578 parser
.add_option("", "--email-server", help="send email via the given server",
579 type='str', default
='localhost')
580 parser
.add_option("", "--always-email", help="always send email, even on success",
582 parser
.add_option("", "--daemon", help="daemonize after initial setup",
584 parser
.add_option("", "--branch", help="the branch to work on (default=master)",
585 default
="master", type='str')
586 parser
.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
587 default
=gitroot
, type='str')
588 parser
.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
589 default
=False, action
="store_true")
590 parser
.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
593 def send_email(subject
, text
, log_tar
):
594 outer
= MIMEMultipart()
595 outer
['Subject'] = subject
596 outer
['To'] = options
.email
597 outer
['From'] = options
.email_from
598 outer
['Date'] = email
.utils
.formatdate(localtime
= True)
599 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
600 outer
.attach(MIMEText(text
, 'plain'))
601 if options
.attach_logs
:
602 fp
= open(log_tar
, 'rb')
603 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
605 # Set the filename parameter
606 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
608 content
= outer
.as_string()
609 s
= smtplib
.SMTP(options
.email_server
)
610 s
.sendmail(options
.email_from
, [options
.email
], content
)
614 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
615 elapsed_time
, log_base
=None, add_log_tail
=True):
616 '''send an email to options.email about the failure'''
617 elapsed_minutes
= elapsed_time
/ 60.0
618 user
= os
.getenv("USER")
624 Your autobuild on %s failed after %.1f minutes
625 when trying to test %s with the following error:
629 the autobuild has been abandoned. Please fix the error and resubmit.
631 A summary of the autobuild process is here:
634 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
636 if options
.restrict_tests
:
638 The build was restricted to tests matching %s\n""" % options
.restrict_tests
640 if failed_task
!= 'rebase':
642 You can see logs of the failed task here:
647 or you can get full logs of all tasks in this job here:
651 The top commit for the tree that was built was:
655 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
658 f
= open("%s/%s.stdout" % (gitroot
, failed_tag
), 'r')
659 lines
= f
.readlines()
660 log_tail
= "".join(lines
[-50:])
661 num_lines
= len(lines
)
663 # Also include stderr (compile failures) if < 50 lines of stdout
664 f
= open("%s/%s.stderr" % (gitroot
, failed_tag
), 'r')
665 log_tail
+= "".join(f
.readlines()[-(50-num_lines
):])
668 The last 50 lines of log messages:
674 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
675 send_email('autobuild[%s] failure on %s for task %s during %s'
676 % (options
.branch
, platform
.node(), failed_task
, failed_stage
),
679 def email_success(elapsed_time
, log_base
=None):
680 '''send an email to options.email about a successful build'''
681 user
= os
.getenv("USER")
687 Your autobuild on %s has succeeded after %.1f minutes.
689 ''' % (platform
.node(), elapsed_time
/ 60.)
691 if options
.restrict_tests
:
693 The build was restricted to tests matching %s\n""" % options
.restrict_tests
698 you can get full logs of all tasks in this job here:
705 The top commit for the tree that was built was:
710 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
711 send_email('autobuild[%s] success on %s' % (options
.branch
, platform
.node()),
715 (options
, args
) = parser
.parse_args()
718 if options
.rebase
is None:
719 raise Exception('You can only use --retry if you also rebase')
721 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
722 test_master
= "%s/master" % testbase
723 test_prefix
= "%s/prefix" % testbase
724 test_tmpdir
= "%s/tmp" % testbase
725 os
.environ
['TMPDIR'] = test_tmpdir
727 # get the top commit message, for emails
728 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
731 os
.makedirs(testbase
)
732 except Exception, reason
:
733 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
734 cleanup_list
.append(testbase
)
737 logfile
= os
.path
.join(testbase
, "log")
738 do_print("Forking into the background, writing progress to %s" % logfile
)
741 write_pidfile(gitroot
+ "/autobuild.pid")
743 start_time
= time
.time()
747 run_cmd("rm -rf %s" % test_master
)
748 run_cmd("rm -rf %s" % test_prefix
)
749 run_cmd("rm -rf %s" % test_tmpdir
)
750 os
.makedirs(test_tmpdir
)
751 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
758 if options
.rebase
is not None:
759 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
761 cleanup_list
.append(gitroot
+ "/autobuild.pid")
763 elapsed_time
= time
.time() - start_time
764 email_failure(-1, 'rebase', 'rebase', 'rebase',
765 'rebase on %s failed' % options
.branch
,
766 elapsed_time
, log_base
=options
.log_base
)
768 blist
= buildlist(args
, options
.rebase
, rebase_branch
=options
.branch
)
771 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
772 if status
!= 0 or errstr
!= "retry":
779 cleanup_list
.append(gitroot
+ "/autobuild.pid")
783 do_print("waiting for tail to flush")
786 elapsed_time
= time
.time() - start_time
789 if options
.passcmd
is not None:
790 do_print("Running passcmd: %s" % options
.passcmd
)
791 run_cmd(options
.passcmd
, dir=test_master
)
792 if options
.pushto
is not None:
793 push_to(options
.pushto
, push_branch
=options
.branch
)
794 if options
.keeplogs
or options
.attach_logs
:
795 blist
.tarlogs("logs.tar.gz")
796 do_print("Logs in logs.tar.gz")
797 if options
.always_email
:
798 email_success(elapsed_time
, log_base
=options
.log_base
)
804 # something failed, gather a tar of the logs
805 blist
.tarlogs("logs.tar.gz")
807 if options
.email
is not None:
808 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
809 elapsed_time
, log_base
=options
.log_base
)
811 elapsed_minutes
= elapsed_time
/ 60.0
814 ####################################################################
818 Your autobuild[%s] on %s failed after %.1f minutes
819 when trying to test %s with the following error:
823 the autobuild has been abandoned. Please fix the error and resubmit.
825 ####################################################################
827 ''' % (options
.branch
, platform
.node(), elapsed_minutes
, failed_task
, errstr
)
831 do_print("Logs in logs.tar.gz")