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" : ".",
35 "samba-nopython" : ".",
38 "talloc" : "lib/talloc",
39 "replace" : "lib/replace",
40 "tevent" : "lib/tevent",
47 defaulttasks
= [ "ctdb", "samba", "samba-xc", "samba-o3", "samba-ctdb", "samba-libs", "samba-static", "samba-systemkrb5", "samba-nopython", "ldb", "tdb", "talloc", "replace", "tevent", "pidl" ]
49 if os
.environ
.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
50 defaulttasks
.remove("samba-o3")
52 ctdb_configure_params
= " --enable-developer --picky-developer ${PREFIX}"
53 samba_configure_params
= " --picky-developer ${PREFIX} ${EXTRA_PYTHON} --with-profiling-data"
55 samba_libs_envvars
= "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH"
56 samba_libs_envvars
+= " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
57 samba_libs_envvars
+= " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
58 samba_libs_configure_base
= samba_libs_envvars
+ " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX} ${EXTRA_PYTHON}"
59 samba_libs_configure_libs
= samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE"
60 samba_libs_configure_samba
= samba_libs_configure_base
+ " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent"
62 if os
.environ
.get("AUTOBUILD_NO_EXTRA_PYTHON", "0") == "1":
65 extra_python
= "--extra-python=/usr/bin/python3"
68 "ctdb" : [ ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
69 ("configure", "./configure " + ctdb_configure_params
, "text/plain"),
70 ("make", "make all", "text/plain"),
71 ("install", "make install", "text/plain"),
72 ("test", "make autotest", "text/plain"),
73 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
74 ("clean", "make clean", "text/plain") ],
76 # We have 'test' before 'install' because, 'test' should work without 'install'
77 "samba" : [ ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
78 ("make", "make -j", "text/plain"),
79 ("test", "make test FAIL_IMMEDIATELY=1", "text/plain"),
80 ("install", "make install", "text/plain"),
81 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
82 ("clean", "make clean", "text/plain") ],
84 "samba-test-only" : [ ("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params
, "text/plain"),
85 ("make", "make -j", "text/plain"),
86 ("test", "make test FAIL_IMMEDIATELY=1 TESTS=${TESTS}", "text/plain") ],
88 # Test cross-compile infrastructure
89 "samba-xc" : [ ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
90 ("configure-cross-execute", "./configure.developer -b ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
91 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params
, "text/plain"),
92 ("configure-cross-answers", "./configure.developer -b ./bin-xa --cross-compile" \
93 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params
, "text/plain"),
94 ("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")],
96 # test build with -O3 -- catches extra warnings and bugs
97 "samba-o3" : [ ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
98 ("configure", "ADDITIONAL_CFLAGS='-O3' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params
, "text/plain"),
99 ("make", "make -j", "text/plain"),
100 ("test", "make quicktest FAIL_IMMEDIATELY=1 TESTS='\(ad_dc\)'", "text/plain"),
101 ("install", "make install", "text/plain"),
102 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
103 ("clean", "make clean", "text/plain") ],
105 "samba-ctdb" : [ ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
107 # make sure we have tdb around:
108 ("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"),
109 ("tdb-make", "cd lib/tdb && make", "text/plain"),
110 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
113 # build samba with cluster support (also building ctdb):
114 ("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"),
115 ("samba-make", "make", "text/plain"),
116 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
117 ("samba-install", "make install", "text/plain"),
118 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
121 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
122 ("clean", "make clean", "text/plain"),
123 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain") ],
126 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
127 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs
, "text/plain"),
128 ("talloc-make", "cd lib/talloc && make", "text/plain"),
129 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
131 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs
, "text/plain"),
132 ("tdb-make", "cd lib/tdb && make", "text/plain"),
133 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
135 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs
, "text/plain"),
136 ("tevent-make", "cd lib/tevent && make", "text/plain"),
137 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
139 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs
, "text/plain"),
140 ("ldb-make", "cd lib/ldb && make", "text/plain"),
141 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
143 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
144 ("nondevel-make", "make -j", "text/plain"),
145 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
146 ("nondevel-install", "make install", "text/plain"),
147 ("nondevel-dist", "make dist", "text/plain"),
149 # retry with all modules shared
150 ("allshared-distclean", "make distclean", "text/plain"),
151 ("allshared-configure", samba_libs_configure_samba
+ " --with-shared-modules=ALL", "text/plain"),
152 ("allshared-make", "make -j", "text/plain")],
155 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
156 # build with all modules static
157 ("allstatic-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=ALL", "text/plain"),
158 ("allstatic-make", "make -j", "text/plain"),
160 # retry without any required modules
161 ("none-distclean", "make distclean", "text/plain"),
162 ("none-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
163 ("none-make", "make -j", "text/plain"),
165 # retry with nonshared smbd and smbtorture
166 ("nonshared-distclean", "make distclean", "text/plain"),
167 ("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"),
168 ("nonshared-make", "make -j", "text/plain")],
170 "samba-systemkrb5" : [
171 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
172 ("configure", "./configure.developer " + samba_configure_params
+ " --with-system-mitkrb5 --without-ad-dc", "text/plain"),
173 ("make", "make -j", "text/plain"),
174 # we currently cannot run a full make test, a limited list of tests could be run
175 # via "make test TESTS=sometests"
176 ("test", "make test FAIL_IMMEDIATELY=1 TESTS='samba3.*ktest'", "text/plain"),
177 ("install", "make install", "text/plain"),
178 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
179 ("clean", "make clean", "text/plain")
182 # Test Samba without python still builds. When this test fails
183 # due to more use of Python, the expectations is that the newly
184 # failing part of the code should be disabled when
185 # --disable-python is set (rather than major work being done to
186 # support this environment). The target here is for vendors
187 # shipping a minimal smbd.
189 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
190 ("configure", "./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
191 ("make", "make -j", "text/plain"),
192 ("install", "make install", "text/plain"),
193 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
194 ("clean", "make clean", "text/plain")
200 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
201 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
202 ("make", "make", "text/plain"),
203 ("install", "make install", "text/plain"),
204 ("test", "make test", "text/plain"),
205 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
206 ("distcheck", "make distcheck", "text/plain"),
207 ("clean", "make clean", "text/plain") ],
210 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
211 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
212 ("make", "make", "text/plain"),
213 ("install", "make install", "text/plain"),
214 ("test", "make test", "text/plain"),
215 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
216 ("distcheck", "make distcheck", "text/plain"),
217 ("clean", "make clean", "text/plain") ],
220 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
221 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
222 ("make", "make", "text/plain"),
223 ("install", "make install", "text/plain"),
224 ("test", "make test", "text/plain"),
225 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
226 ("distcheck", "make distcheck", "text/plain"),
227 ("clean", "make clean", "text/plain") ],
230 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
231 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
232 ("make", "make", "text/plain"),
233 ("install", "make install", "text/plain"),
234 ("test", "make test", "text/plain"),
235 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
236 ("distcheck", "make distcheck", "text/plain"),
237 ("clean", "make clean", "text/plain") ],
240 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
241 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
242 ("make", "make", "text/plain"),
243 ("install", "make install", "text/plain"),
244 ("test", "make test", "text/plain"),
245 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
246 ("distcheck", "make distcheck", "text/plain"),
247 ("clean", "make clean", "text/plain") ],
250 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
251 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
252 ("touch", "touch *.yp", "text/plain"),
253 ("make", "make", "text/plain"),
254 ("test", "make test", "text/plain"),
255 ("install", "make install", "text/plain"),
256 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
257 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
258 ("clean", "make clean", "text/plain") ],
260 # these are useful for debugging autobuild
261 'pass' : [ ("pass", 'echo passing && /bin/true', "text/plain") ],
262 'fail' : [ ("fail", 'echo failing && /bin/false', "text/plain") ]
270 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
272 show
= options
.verbose
274 do_print("Running: '%s' in '%s'" % (cmd
, dir))
276 return Popen([cmd
], shell
=True, stdout
=PIPE
, cwd
=dir).communicate()[0]
278 return check_call(cmd
, shell
=True, cwd
=dir)
280 return call(cmd
, shell
=True, cwd
=dir)
283 class builder(object):
284 '''handle build of one directory'''
286 def __init__(self
, name
, sequence
, cp
=True):
288 self
.dir = builddirs
[name
]
290 self
.tag
= self
.name
.replace('/', '_')
291 self
.sequence
= sequence
293 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
294 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
296 do_print("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
297 do_print("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
298 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
299 self
.stdout
= open(self
.stdout_path
, 'w')
300 self
.stderr
= open(self
.stderr_path
, 'w')
301 self
.stdin
= open("/dev/null", 'r')
302 self
.sdir
= "%s/%s" % (testbase
, self
.tag
)
303 self
.prefix
= "%s/%s" % (test_prefix
, self
.tag
)
304 run_cmd("rm -rf %s" % self
.sdir
)
305 run_cmd("rm -rf %s" % self
.prefix
)
307 run_cmd("cp --recursive --link --archive %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
309 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
312 def start_next(self
):
313 if self
.next
== len(self
.sequence
):
314 if not options
.nocleanup
:
315 run_cmd("rm -rf %s" % self
.sdir
)
316 run_cmd("rm -rf %s" % self
.prefix
)
317 do_print('%s: Completed OK' % self
.name
)
320 (self
.stage
, self
.cmd
, self
.output_mime_type
) = self
.sequence
[self
.next
]
321 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib
=1, prefix
=self
.prefix
))
322 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
323 self
.cmd
= self
.cmd
.replace("${EXTRA_PYTHON}", "%s" % extra_python
)
324 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
325 self
.cmd
= self
.cmd
.replace("${TESTS}", options
.restrict_tests
)
326 # if self.output_mime_type == "text/x-subunit":
327 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
328 do_print('%s: [%s] Running %s' % (self
.name
, self
.stage
, self
.cmd
))
330 os
.chdir("%s/%s" % (self
.sdir
, self
.dir))
331 self
.proc
= Popen(self
.cmd
, shell
=True,
332 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
337 class buildlist(object):
338 '''handle build of multiple directories'''
340 def __init__(self
, tasknames
, rebase_url
, rebase_branch
="master"):
343 self
.tail_proc
= None
346 if options
.restrict_tests
:
347 tasknames
= ["samba-test-only"]
349 tasknames
= defaulttasks
351 # If we are only running one test,
352 # do not sleep randomly to wait for it to start
353 os
.environ
['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
356 b
= builder(n
, tasks
[n
], cp
=n
is not "pidl")
359 rebase_remote
= "rebaseon"
360 retry_task
= [ ("retry",
362 git remote add -t %s %s %s
366 git describe %s/%s > old_remote_branch.desc
368 git describe %s/%s > remote_branch.desc
369 diff old_remote_branch.desc remote_branch.desc
372 rebase_branch
, rebase_remote
, rebase_url
,
374 rebase_remote
, rebase_branch
,
376 rebase_remote
, rebase_branch
380 self
.retry
= builder('retry', retry_task
, cp
=False)
381 self
.need_retry
= False
384 if self
.tail_proc
is not None:
385 self
.tail_proc
.terminate()
386 self
.tail_proc
.wait()
387 self
.tail_proc
= None
388 if self
.retry
is not None:
389 self
.retry
.proc
.terminate()
390 self
.retry
.proc
.wait()
393 if b
.proc
is not None:
394 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.sdir
, checkfail
=False)
406 b
.status
= b
.proc
.poll()
412 ret
= self
.retry
.proc
.poll()
414 self
.need_retry
= True
424 if options
.retry
and self
.need_retry
:
426 do_print("retry needed")
427 return (0, None, None, None, "retry")
430 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
432 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
435 return (0, None, None, None, "All OK")
437 def write_system_info(self
):
438 filename
= 'system-info.txt'
439 f
= open(filename
, 'w')
440 for cmd
in ['uname -a', 'free', 'cat /proc/cpuinfo']:
441 print >>f
, '### %s' % cmd
442 print >>f
, run_cmd(cmd
, output
=True, checkfail
=False)
447 def tarlogs(self
, fname
):
448 tar
= tarfile
.open(fname
, "w:gz")
450 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
451 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
452 if os
.path
.exists("autobuild.log"):
453 tar
.add("autobuild.log")
454 sys_info
= self
.write_system_info()
458 def remove_logs(self
):
460 os
.unlink(b
.stdout_path
)
461 os
.unlink(b
.stderr_path
)
463 def start_tail(self
):
465 cmd
= "tail -f *.stdout *.stderr"
467 self
.tail_proc
= Popen(cmd
, shell
=True)
472 if options
.nocleanup
:
474 run_cmd("stat %s || true" % test_tmpdir
, show
=True)
475 run_cmd("stat %s" % testbase
, show
=True)
476 do_print("Cleaning up ....")
477 for d
in cleanup_list
:
478 run_cmd("rm -rf %s" % d
)
482 '''get to the top of the git repo'''
485 if os
.path
.isdir(os
.path
.join(p
, ".git")):
487 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
491 def daemonize(logfile
):
493 if pid
== 0: # Parent
496 if pid
!= 0: # Actual daemon
501 import resource
# Resource usage information.
502 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
503 if maxfd
== resource
.RLIM_INFINITY
:
504 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
505 for fd
in range(0, maxfd
):
510 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
514 def write_pidfile(fname
):
515 '''write a pid file, cleanup on exit'''
516 f
= open(fname
, mode
='w')
517 f
.write("%u\n" % os
.getpid())
521 def rebase_tree(rebase_url
, rebase_branch
= "master"):
522 rebase_remote
= "rebaseon"
523 do_print("Rebasing on %s" % rebase_url
)
524 run_cmd("git describe HEAD", show
=True, dir=test_master
)
525 run_cmd("git remote add -t %s %s %s" %
526 (rebase_branch
, rebase_remote
, rebase_url
),
527 show
=True, dir=test_master
)
528 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
529 if options
.fix_whitespace
:
530 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
531 (rebase_remote
, rebase_branch
),
532 show
=True, dir=test_master
)
534 run_cmd("git rebase --force-rebase %s/%s" %
535 (rebase_remote
, rebase_branch
),
536 show
=True, dir=test_master
)
537 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
538 (rebase_remote
, rebase_branch
),
539 dir=test_master
, output
=True)
541 do_print("No differences between HEAD and %s/%s - exiting" %
542 (rebase_remote
, rebase_branch
))
544 run_cmd("git describe %s/%s" %
545 (rebase_remote
, rebase_branch
),
546 show
=True, dir=test_master
)
547 run_cmd("git describe HEAD", show
=True, dir=test_master
)
548 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
549 (rebase_remote
, rebase_branch
),
550 show
=True, dir=test_master
)
552 def push_to(push_url
, push_branch
= "master"):
553 push_remote
= "pushto"
554 do_print("Pushing to %s" % push_url
)
556 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
557 run_cmd("git commit --amend -c HEAD", dir=test_master
)
558 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
559 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
560 run_cmd("git remote add -t %s %s %s" %
561 (push_branch
, push_remote
, push_url
),
562 show
=True, dir=test_master
)
563 run_cmd("git push %s +HEAD:%s" %
564 (push_remote
, push_branch
),
565 show
=True, dir=test_master
)
567 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
569 gitroot
= find_git_root()
571 raise Exception("Failed to find git root")
573 parser
= OptionParser()
574 parser
.add_option("", "--tail", help="show output while running", default
=False, action
="store_true")
575 parser
.add_option("", "--keeplogs", help="keep logs", default
=False, action
="store_true")
576 parser
.add_option("", "--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
577 parser
.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase
,
578 default
=def_testbase
)
579 parser
.add_option("", "--passcmd", help="command to run on success", default
=None)
580 parser
.add_option("", "--verbose", help="show all commands as they are run",
581 default
=False, action
="store_true")
582 parser
.add_option("", "--rebase", help="rebase on the given tree before testing",
583 default
=None, type='str')
584 parser
.add_option("", "--pushto", help="push to a git url on success",
585 default
=None, type='str')
586 parser
.add_option("", "--mark", help="add a Tested-By signoff before pushing",
587 default
=False, action
="store_true")
588 parser
.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
589 default
=False, action
="store_true")
590 parser
.add_option("", "--retry", help="automatically retry if master changes",
591 default
=False, action
="store_true")
592 parser
.add_option("", "--email", help="send email to the given address on failure",
593 type='str', default
=None)
594 parser
.add_option("", "--email-from", help="send email from the given address",
595 type='str', default
="autobuild@samba.org")
596 parser
.add_option("", "--email-server", help="send email via the given server",
597 type='str', default
='localhost')
598 parser
.add_option("", "--always-email", help="always send email, even on success",
600 parser
.add_option("", "--daemon", help="daemonize after initial setup",
602 parser
.add_option("", "--branch", help="the branch to work on (default=master)",
603 default
="master", type='str')
604 parser
.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
605 default
=gitroot
, type='str')
606 parser
.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
607 default
=False, action
="store_true")
608 parser
.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
611 def send_email(subject
, text
, log_tar
):
612 outer
= MIMEMultipart()
613 outer
['Subject'] = subject
614 outer
['To'] = options
.email
615 outer
['From'] = options
.email_from
616 outer
['Date'] = email
.utils
.formatdate(localtime
= True)
617 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
618 outer
.attach(MIMEText(text
, 'plain'))
619 if options
.attach_logs
:
620 fp
= open(log_tar
, 'rb')
621 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
623 # Set the filename parameter
624 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
626 content
= outer
.as_string()
627 s
= smtplib
.SMTP(options
.email_server
)
628 s
.sendmail(options
.email_from
, [options
.email
], content
)
632 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
633 elapsed_time
, log_base
=None, add_log_tail
=True):
634 '''send an email to options.email about the failure'''
635 elapsed_minutes
= elapsed_time
/ 60.0
636 user
= os
.getenv("USER")
642 Your autobuild on %s failed after %.1f minutes
643 when trying to test %s with the following error:
647 the autobuild has been abandoned. Please fix the error and resubmit.
649 A summary of the autobuild process is here:
652 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
654 if options
.restrict_tests
:
656 The build was restricted to tests matching %s\n""" % options
.restrict_tests
658 if failed_task
!= 'rebase':
660 You can see logs of the failed task here:
665 or you can get full logs of all tasks in this job here:
669 The top commit for the tree that was built was:
673 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
676 f
= open("%s/%s.stdout" % (gitroot
, failed_tag
), 'r')
677 lines
= f
.readlines()
678 log_tail
= "".join(lines
[-50:])
679 num_lines
= len(lines
)
681 # Also include stderr (compile failures) if < 50 lines of stdout
682 f
= open("%s/%s.stderr" % (gitroot
, failed_tag
), 'r')
683 log_tail
+= "".join(f
.readlines()[-(50-num_lines
):])
686 The last 50 lines of log messages:
692 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
693 send_email('autobuild[%s] failure on %s for task %s during %s'
694 % (options
.branch
, platform
.node(), failed_task
, failed_stage
),
697 def email_success(elapsed_time
, log_base
=None):
698 '''send an email to options.email about a successful build'''
699 user
= os
.getenv("USER")
705 Your autobuild on %s has succeeded after %.1f minutes.
707 ''' % (platform
.node(), elapsed_time
/ 60.)
709 if options
.restrict_tests
:
711 The build was restricted to tests matching %s\n""" % options
.restrict_tests
716 you can get full logs of all tasks in this job here:
723 The top commit for the tree that was built was:
728 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
729 send_email('autobuild[%s] success on %s' % (options
.branch
, platform
.node()),
733 (options
, args
) = parser
.parse_args()
736 if options
.rebase
is None:
737 raise Exception('You can only use --retry if you also rebase')
739 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
740 test_master
= "%s/master" % testbase
741 test_prefix
= "%s/prefix" % testbase
742 test_tmpdir
= "%s/tmp" % testbase
743 os
.environ
['TMPDIR'] = test_tmpdir
745 # get the top commit message, for emails
746 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
749 os
.makedirs(testbase
)
750 except Exception, reason
:
751 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
752 cleanup_list
.append(testbase
)
755 logfile
= os
.path
.join(testbase
, "log")
756 do_print("Forking into the background, writing progress to %s" % logfile
)
759 write_pidfile(gitroot
+ "/autobuild.pid")
761 start_time
= time
.time()
765 run_cmd("rm -rf %s" % test_tmpdir
, show
=True)
766 os
.makedirs(test_tmpdir
)
767 # The waf uninstall code removes empty directories all the way
768 # up the tree. Creating a file in test_tmpdir stops it from
770 run_cmd("touch %s" % os
.path
.join(test_tmpdir
,
771 ".directory-is-not-empty"), show
=True)
772 run_cmd("stat %s" % test_tmpdir
, show
=True)
773 run_cmd("stat %s" % testbase
, show
=True)
774 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
781 if options
.rebase
is not None:
782 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
784 cleanup_list
.append(gitroot
+ "/autobuild.pid")
786 elapsed_time
= time
.time() - start_time
787 email_failure(-1, 'rebase', 'rebase', 'rebase',
788 'rebase on %s failed' % options
.branch
,
789 elapsed_time
, log_base
=options
.log_base
)
791 blist
= buildlist(args
, options
.rebase
, rebase_branch
=options
.branch
)
794 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
795 if status
!= 0 or errstr
!= "retry":
802 cleanup_list
.append(gitroot
+ "/autobuild.pid")
808 do_print("waiting for tail to flush")
811 elapsed_time
= time
.time() - start_time
813 if options
.passcmd
is not None:
814 do_print("Running passcmd: %s" % options
.passcmd
)
815 run_cmd(options
.passcmd
, dir=test_master
)
816 if options
.pushto
is not None:
817 push_to(options
.pushto
, push_branch
=options
.branch
)
818 if options
.keeplogs
or options
.attach_logs
:
819 blist
.tarlogs("logs.tar.gz")
820 do_print("Logs in logs.tar.gz")
821 if options
.always_email
:
822 email_success(elapsed_time
, log_base
=options
.log_base
)
828 # something failed, gather a tar of the logs
829 blist
.tarlogs("logs.tar.gz")
831 if options
.email
is not None:
832 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
833 elapsed_time
, log_base
=options
.log_base
)
835 elapsed_minutes
= elapsed_time
/ 60.0
838 ####################################################################
842 Your autobuild[%s] on %s failed after %.1f minutes
843 when trying to test %s with the following error:
847 the autobuild has been abandoned. Please fix the error and resubmit.
849 ####################################################################
851 ''' % (options
.branch
, platform
.node(), elapsed_minutes
, failed_task
, errstr
)
855 do_print("Logs in logs.tar.gz")