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 run_cmd("stat %s || true" % test_tmpdir
, show
=True)
459 run_cmd("stat %s" % testbase
, show
=True)
460 do_print("Cleaning up ....")
461 for d
in cleanup_list
:
462 run_cmd("rm -rf %s" % d
)
466 '''get to the top of the git repo'''
469 if os
.path
.isdir(os
.path
.join(p
, ".git")):
471 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
475 def daemonize(logfile
):
477 if pid
== 0: # Parent
480 if pid
!= 0: # Actual daemon
485 import resource
# Resource usage information.
486 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
487 if maxfd
== resource
.RLIM_INFINITY
:
488 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
489 for fd
in range(0, maxfd
):
494 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
498 def write_pidfile(fname
):
499 '''write a pid file, cleanup on exit'''
500 f
= open(fname
, mode
='w')
501 f
.write("%u\n" % os
.getpid())
505 def rebase_tree(rebase_url
, rebase_branch
= "master"):
506 rebase_remote
= "rebaseon"
507 do_print("Rebasing on %s" % rebase_url
)
508 run_cmd("git describe HEAD", show
=True, dir=test_master
)
509 run_cmd("git remote add -t %s %s %s" %
510 (rebase_branch
, rebase_remote
, rebase_url
),
511 show
=True, dir=test_master
)
512 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
513 if options
.fix_whitespace
:
514 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
515 (rebase_remote
, rebase_branch
),
516 show
=True, dir=test_master
)
518 run_cmd("git rebase --force-rebase %s/%s" %
519 (rebase_remote
, rebase_branch
),
520 show
=True, dir=test_master
)
521 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
522 (rebase_remote
, rebase_branch
),
523 dir=test_master
, output
=True)
525 do_print("No differences between HEAD and %s/%s - exiting" %
526 (rebase_remote
, rebase_branch
))
528 run_cmd("git describe %s/%s" %
529 (rebase_remote
, rebase_branch
),
530 show
=True, dir=test_master
)
531 run_cmd("git describe HEAD", show
=True, dir=test_master
)
532 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
533 (rebase_remote
, rebase_branch
),
534 show
=True, dir=test_master
)
536 def push_to(push_url
, push_branch
= "master"):
537 push_remote
= "pushto"
538 do_print("Pushing to %s" % push_url
)
540 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
541 run_cmd("git commit --amend -c HEAD", dir=test_master
)
542 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
543 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
544 run_cmd("git remote add -t %s %s %s" %
545 (push_branch
, push_remote
, push_url
),
546 show
=True, dir=test_master
)
547 run_cmd("git push %s +HEAD:%s" %
548 (push_remote
, push_branch
),
549 show
=True, dir=test_master
)
551 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
553 gitroot
= find_git_root()
555 raise Exception("Failed to find git root")
557 parser
= OptionParser()
558 parser
.add_option("", "--tail", help="show output while running", default
=False, action
="store_true")
559 parser
.add_option("", "--keeplogs", help="keep logs", default
=False, action
="store_true")
560 parser
.add_option("", "--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
561 parser
.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase
,
562 default
=def_testbase
)
563 parser
.add_option("", "--passcmd", help="command to run on success", default
=None)
564 parser
.add_option("", "--verbose", help="show all commands as they are run",
565 default
=False, action
="store_true")
566 parser
.add_option("", "--rebase", help="rebase on the given tree before testing",
567 default
=None, type='str')
568 parser
.add_option("", "--pushto", help="push to a git url on success",
569 default
=None, type='str')
570 parser
.add_option("", "--mark", help="add a Tested-By signoff before pushing",
571 default
=False, action
="store_true")
572 parser
.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
573 default
=False, action
="store_true")
574 parser
.add_option("", "--retry", help="automatically retry if master changes",
575 default
=False, action
="store_true")
576 parser
.add_option("", "--email", help="send email to the given address on failure",
577 type='str', default
=None)
578 parser
.add_option("", "--email-from", help="send email from the given address",
579 type='str', default
="autobuild@samba.org")
580 parser
.add_option("", "--email-server", help="send email via the given server",
581 type='str', default
='localhost')
582 parser
.add_option("", "--always-email", help="always send email, even on success",
584 parser
.add_option("", "--daemon", help="daemonize after initial setup",
586 parser
.add_option("", "--branch", help="the branch to work on (default=master)",
587 default
="master", type='str')
588 parser
.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
589 default
=gitroot
, type='str')
590 parser
.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
591 default
=False, action
="store_true")
592 parser
.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
595 def send_email(subject
, text
, log_tar
):
596 outer
= MIMEMultipart()
597 outer
['Subject'] = subject
598 outer
['To'] = options
.email
599 outer
['From'] = options
.email_from
600 outer
['Date'] = email
.utils
.formatdate(localtime
= True)
601 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
602 outer
.attach(MIMEText(text
, 'plain'))
603 if options
.attach_logs
:
604 fp
= open(log_tar
, 'rb')
605 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
607 # Set the filename parameter
608 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
610 content
= outer
.as_string()
611 s
= smtplib
.SMTP(options
.email_server
)
612 s
.sendmail(options
.email_from
, [options
.email
], content
)
616 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
617 elapsed_time
, log_base
=None, add_log_tail
=True):
618 '''send an email to options.email about the failure'''
619 elapsed_minutes
= elapsed_time
/ 60.0
620 user
= os
.getenv("USER")
626 Your autobuild on %s failed after %.1f minutes
627 when trying to test %s with the following error:
631 the autobuild has been abandoned. Please fix the error and resubmit.
633 A summary of the autobuild process is here:
636 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
638 if options
.restrict_tests
:
640 The build was restricted to tests matching %s\n""" % options
.restrict_tests
642 if failed_task
!= 'rebase':
644 You can see logs of the failed task here:
649 or you can get full logs of all tasks in this job here:
653 The top commit for the tree that was built was:
657 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
660 f
= open("%s/%s.stdout" % (gitroot
, failed_tag
), 'r')
661 lines
= f
.readlines()
662 log_tail
= "".join(lines
[-50:])
663 num_lines
= len(lines
)
665 # Also include stderr (compile failures) if < 50 lines of stdout
666 f
= open("%s/%s.stderr" % (gitroot
, failed_tag
), 'r')
667 log_tail
+= "".join(f
.readlines()[-(50-num_lines
):])
670 The last 50 lines of log messages:
676 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
677 send_email('autobuild[%s] failure on %s for task %s during %s'
678 % (options
.branch
, platform
.node(), failed_task
, failed_stage
),
681 def email_success(elapsed_time
, log_base
=None):
682 '''send an email to options.email about a successful build'''
683 user
= os
.getenv("USER")
689 Your autobuild on %s has succeeded after %.1f minutes.
691 ''' % (platform
.node(), elapsed_time
/ 60.)
693 if options
.restrict_tests
:
695 The build was restricted to tests matching %s\n""" % options
.restrict_tests
700 you can get full logs of all tasks in this job here:
707 The top commit for the tree that was built was:
712 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
713 send_email('autobuild[%s] success on %s' % (options
.branch
, platform
.node()),
717 (options
, args
) = parser
.parse_args()
720 if options
.rebase
is None:
721 raise Exception('You can only use --retry if you also rebase')
723 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
724 test_master
= "%s/master" % testbase
725 test_prefix
= "%s/prefix" % testbase
726 test_tmpdir
= "%s/tmp" % testbase
727 os
.environ
['TMPDIR'] = test_tmpdir
729 # get the top commit message, for emails
730 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
733 os
.makedirs(testbase
)
734 except Exception, reason
:
735 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
736 cleanup_list
.append(testbase
)
739 logfile
= os
.path
.join(testbase
, "log")
740 do_print("Forking into the background, writing progress to %s" % logfile
)
743 write_pidfile(gitroot
+ "/autobuild.pid")
745 start_time
= time
.time()
749 run_cmd("rm -rf %s" % test_tmpdir
, show
=True)
750 os
.makedirs(test_tmpdir
)
751 run_cmd("stat %s" % test_tmpdir
, show
=True)
752 run_cmd("stat %s" % testbase
, show
=True)
753 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
760 if options
.rebase
is not None:
761 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
763 cleanup_list
.append(gitroot
+ "/autobuild.pid")
765 elapsed_time
= time
.time() - start_time
766 email_failure(-1, 'rebase', 'rebase', 'rebase',
767 'rebase on %s failed' % options
.branch
,
768 elapsed_time
, log_base
=options
.log_base
)
770 blist
= buildlist(args
, options
.rebase
, rebase_branch
=options
.branch
)
773 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
774 if status
!= 0 or errstr
!= "retry":
781 cleanup_list
.append(gitroot
+ "/autobuild.pid")
787 do_print("waiting for tail to flush")
790 elapsed_time
= time
.time() - start_time
792 if options
.passcmd
is not None:
793 do_print("Running passcmd: %s" % options
.passcmd
)
794 run_cmd(options
.passcmd
, dir=test_master
)
795 if options
.pushto
is not None:
796 push_to(options
.pushto
, push_branch
=options
.branch
)
797 if options
.keeplogs
or options
.attach_logs
:
798 blist
.tarlogs("logs.tar.gz")
799 do_print("Logs in logs.tar.gz")
800 if options
.always_email
:
801 email_success(elapsed_time
, log_base
=options
.log_base
)
807 # something failed, gather a tar of the logs
808 blist
.tarlogs("logs.tar.gz")
810 if options
.email
is not None:
811 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
812 elapsed_time
, log_base
=options
.log_base
)
814 elapsed_minutes
= elapsed_time
/ 60.0
817 ####################################################################
821 Your autobuild[%s] on %s failed after %.1f minutes
822 when trying to test %s with the following error:
826 the autobuild has been abandoned. Please fix the error and resubmit.
828 ####################################################################
830 ''' % (options
.branch
, platform
.node(), elapsed_minutes
, failed_task
, errstr
)
834 do_print("Logs in logs.tar.gz")