source: vendor/current/script/autobuild.py@ 988

Last change on this file since 988 was 988, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.3

File size: 30.7 KB
Line 
1#!/usr/bin/env python
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
5
6from subprocess import call, check_call,Popen, PIPE
7import os, tarfile, sys, time
8from optparse import OptionParser
9import smtplib
10import email
11from email.mime.text import MIMEText
12from email.mime.base import MIMEBase
13from email.mime.application import MIMEApplication
14from email.mime.multipart import MIMEMultipart
15from distutils.sysconfig import get_python_lib
16import platform
17
18# This speeds up testing remarkably.
19os.environ['TDB_NO_FSYNC'] = '1'
20
21cleanup_list = []
22
23builddirs = {
24 "ctdb" : "ctdb",
25 "samba" : ".",
26 "samba-xc" : ".",
27 "samba-ctdb" : ".",
28 "samba-libs" : ".",
29 "samba-static" : ".",
30 "ldb" : "lib/ldb",
31 "tdb" : "lib/tdb",
32 "talloc" : "lib/talloc",
33 "replace" : "lib/replace",
34 "tevent" : "lib/tevent",
35 "pidl" : "pidl",
36 "pass" : ".",
37 "fail" : ".",
38 "retry" : "."
39 }
40
41defaulttasks = [ "ctdb", "samba", "samba-xc", "samba-ctdb", "samba-libs", "samba-static", "ldb", "tdb", "talloc", "replace", "tevent", "pidl" ]
42
43samba_configure_params = " --picky-developer ${PREFIX} --with-profiling-data"
44
45samba_libs_envvars = "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH"
46samba_libs_envvars += " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
47samba_libs_envvars += " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
48samba_libs_configure_base = samba_libs_envvars + " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX}"
49samba_libs_configure_libs = samba_libs_configure_base + " --bundled-libraries=NONE"
50samba_libs_configure_samba = samba_libs_configure_base + " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent"
51
52tasks = {
53 "ctdb" : [ ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
54 ("configure", "./configure ${PREFIX}", "text/plain"),
55 ("make", "make all", "text/plain"),
56 ("install", "make install", "text/plain"),
57 ("test", "make autotest", "text/plain"),
58 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
59 ("clean", "make clean", "text/plain") ],
60
61 # We have 'test' before 'install' because, 'test' should work without 'install'
62 "samba" : [ ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
63 ("make", "make -j", "text/plain"),
64 ("test", "make test FAIL_IMMEDIATELY=1", "text/plain"),
65 ("install", "make install", "text/plain"),
66 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
67 ("clean", "make clean", "text/plain") ],
68
69 # Test cross-compile infrastructure
70 "samba-xc" : [ ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
71 ("configure-cross-execute", "./configure.developer -b ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
72 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params, "text/plain"),
73 ("configure-cross-answers", "./configure.developer -b ./bin-xa --cross-compile" \
74 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params, "text/plain"),
75 ("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")],
76
77
78 "samba-ctdb" : [ ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
79
80 # make sure we have tdb around:
81 ("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"),
82 ("tdb-make", "cd lib/tdb && make", "text/plain"),
83 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
84
85
86 # build samba with cluster support (also building ctdb):
87 ("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"),
88 ("samba-make", "make", "text/plain"),
89 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
90 ("samba-install", "make install", "text/plain"),
91 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
92
93 # clean up:
94 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
95 ("clean", "make clean", "text/plain"),
96 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain") ],
97
98 "samba-libs" : [
99 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
100 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs, "text/plain"),
101 ("talloc-make", "cd lib/talloc && make", "text/plain"),
102 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
103
104 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs, "text/plain"),
105 ("tdb-make", "cd lib/tdb && make", "text/plain"),
106 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
107
108 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs, "text/plain"),
109 ("tevent-make", "cd lib/tevent && make", "text/plain"),
110 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
111
112 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs, "text/plain"),
113 ("ldb-make", "cd lib/ldb && make", "text/plain"),
114 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
115
116 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
117 ("nondevel-make", "make -j", "text/plain"),
118 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
119 ("nondevel-install", "make install", "text/plain"),
120 ("nondevel-dist", "make dist", "text/plain"),
121
122 # retry with all modules shared
123 ("allshared-distclean", "make distclean", "text/plain"),
124 ("allshared-configure", samba_libs_configure_samba + " --with-shared-modules=ALL", "text/plain"),
125 ("allshared-make", "make -j", "text/plain")],
126
127 "samba-static" : [
128 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
129 # build with all modules static
130 ("allstatic-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=ALL", "text/plain"),
131 ("allstatic-make", "make -j", "text/plain"),
132
133 # retry without any required modules
134 ("none-distclean", "make distclean", "text/plain"),
135 ("none-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
136 ("none-make", "make -j", "text/plain"),
137
138 # retry with nonshared smbd and smbtorture
139 ("nonshared-distclean", "make distclean", "text/plain"),
140 ("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"),
141 ("nonshared-make", "make -j", "text/plain")],
142
143 "ldb" : [
144 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
145 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
146 ("make", "make", "text/plain"),
147 ("install", "make install", "text/plain"),
148 ("test", "make test", "text/plain"),
149 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
150 ("distcheck", "make distcheck", "text/plain"),
151 ("clean", "make clean", "text/plain") ],
152
153 "tdb" : [
154 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
155 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
156 ("make", "make", "text/plain"),
157 ("install", "make install", "text/plain"),
158 ("test", "make test", "text/plain"),
159 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
160 ("distcheck", "make distcheck", "text/plain"),
161 ("clean", "make clean", "text/plain") ],
162
163 "talloc" : [
164 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
165 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
166 ("make", "make", "text/plain"),
167 ("install", "make install", "text/plain"),
168 ("test", "make test", "text/plain"),
169 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
170 ("distcheck", "make distcheck", "text/plain"),
171 ("clean", "make clean", "text/plain") ],
172
173 "replace" : [
174 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
175 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
176 ("make", "make", "text/plain"),
177 ("install", "make install", "text/plain"),
178 ("test", "make test", "text/plain"),
179 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
180 ("distcheck", "make distcheck", "text/plain"),
181 ("clean", "make clean", "text/plain") ],
182
183 "tevent" : [
184 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
185 ("configure", "./configure --enable-developer -C ${PREFIX}", "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") ],
192
193 "pidl" : [
194 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
195 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
196 ("touch", "touch *.yp", "text/plain"),
197 ("make", "make", "text/plain"),
198 ("test", "make test", "text/plain"),
199 ("install", "make install", "text/plain"),
200 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
201 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
202 ("clean", "make clean", "text/plain") ],
203
204 # these are useful for debugging autobuild
205 'pass' : [ ("pass", 'echo passing && /bin/true', "text/plain") ],
206 'fail' : [ ("fail", 'echo failing && /bin/false', "text/plain") ]
207}
208
209def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
210 if show is None:
211 show = options.verbose
212 if show:
213 print("Running: '%s' in '%s'" % (cmd, dir))
214 if output:
215 return Popen([cmd], shell=True, stdout=PIPE, cwd=dir).communicate()[0]
216 elif checkfail:
217 return check_call(cmd, shell=True, cwd=dir)
218 else:
219 return call(cmd, shell=True, cwd=dir)
220
221
222class builder(object):
223 '''handle build of one directory'''
224
225 def __init__(self, name, sequence, cp=True):
226 self.name = name
227 self.dir = builddirs[name]
228
229 self.tag = self.name.replace('/', '_')
230 self.sequence = sequence
231 self.next = 0
232 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
233 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
234 if options.verbose:
235 print("stdout for %s in %s" % (self.name, self.stdout_path))
236 print("stderr for %s in %s" % (self.name, self.stderr_path))
237 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
238 self.stdout = open(self.stdout_path, 'w')
239 self.stderr = open(self.stderr_path, 'w')
240 self.stdin = open("/dev/null", 'r')
241 self.sdir = "%s/%s" % (testbase, self.tag)
242 self.prefix = "%s/prefix/%s" % (testbase, self.tag)
243 run_cmd("rm -rf %s" % self.sdir)
244 cleanup_list.append(self.sdir)
245 cleanup_list.append(self.prefix)
246 os.makedirs(self.sdir)
247 run_cmd("rm -rf %s" % self.sdir)
248 if cp:
249 run_cmd("cp --recursive --link --archive %s %s" % (test_master, self.sdir), dir=test_master, show=True)
250 else:
251 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.sdir), dir=test_master, show=True)
252 self.start_next()
253
254 def start_next(self):
255 if self.next == len(self.sequence):
256 print '%s: Completed OK' % self.name
257 self.done = True
258 return
259 (self.stage, self.cmd, self.output_mime_type) = self.sequence[self.next]
260 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib=1, prefix=self.prefix))
261 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
262 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
263# if self.output_mime_type == "text/x-subunit":
264# self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
265 print '%s: [%s] Running %s' % (self.name, self.stage, self.cmd)
266 cwd = os.getcwd()
267 os.chdir("%s/%s" % (self.sdir, self.dir))
268 self.proc = Popen(self.cmd, shell=True,
269 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
270 os.chdir(cwd)
271 self.next += 1
272
273
274class buildlist(object):
275 '''handle build of multiple directories'''
276
277 def __init__(self, tasklist, tasknames, rebase_url, rebase_branch="master"):
278 global tasks
279 self.tlist = []
280 self.tail_proc = None
281 self.retry = None
282 if tasknames == []:
283 tasknames = defaulttasks
284 else:
285 # If we are only running one test,
286 # do not sleep randomly to wait for it to start
287 os.environ['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
288
289 for n in tasknames:
290 b = builder(n, tasks[n], cp=n is not "pidl")
291 self.tlist.append(b)
292 if options.retry:
293 rebase_remote = "rebaseon"
294 retry_task = [ ("retry",
295 '''set -e
296 git remote add -t %s %s %s
297 git fetch %s
298 while :; do
299 sleep 60
300 git describe %s/%s > old_remote_branch.desc
301 git fetch %s
302 git describe %s/%s > remote_branch.desc
303 diff old_remote_branch.desc remote_branch.desc
304 done
305 ''' % (
306 rebase_branch, rebase_remote, rebase_url,
307 rebase_remote,
308 rebase_remote, rebase_branch,
309 rebase_remote,
310 rebase_remote, rebase_branch
311 ),
312 "test/plain" ) ]
313
314 self.retry = builder('retry', retry_task, cp=False)
315 self.need_retry = False
316
317 def kill_kids(self):
318 if self.tail_proc is not None:
319 self.tail_proc.terminate()
320 self.tail_proc.wait()
321 self.tail_proc = None
322 if self.retry is not None:
323 self.retry.proc.terminate()
324 self.retry.proc.wait()
325 self.retry = None
326 for b in self.tlist:
327 if b.proc is not None:
328 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.sdir, checkfail=False)
329 b.proc.terminate()
330 b.proc.wait()
331 b.proc = None
332
333 def wait_one(self):
334 while True:
335 none_running = True
336 for b in self.tlist:
337 if b.proc is None:
338 continue
339 none_running = False
340 b.status = b.proc.poll()
341 if b.status is None:
342 continue
343 b.proc = None
344 return b
345 if options.retry:
346 ret = self.retry.proc.poll()
347 if ret is not None:
348 self.need_retry = True
349 self.retry = None
350 return None
351 if none_running:
352 return None
353 time.sleep(0.1)
354
355 def run(self):
356 while True:
357 b = self.wait_one()
358 if options.retry and self.need_retry:
359 self.kill_kids()
360 print("retry needed")
361 return (0, None, None, None, "retry")
362 if b is None:
363 break
364 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
365 self.kill_kids()
366 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
367 b.start_next()
368 self.kill_kids()
369 return (0, None, None, None, "All OK")
370
371 def write_system_info(self):
372 filename = 'system-info.txt'
373 f = open(filename, 'w')
374 for cmd in ['uname -a', 'free', 'cat /proc/cpuinfo']:
375 print >>f, '### %s' % cmd
376 print >>f, run_cmd(cmd, output=True, checkfail=False)
377 print >>f
378 f.close()
379 return filename
380
381 def tarlogs(self, fname):
382 tar = tarfile.open(fname, "w:gz")
383 for b in self.tlist:
384 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
385 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
386 if os.path.exists("autobuild.log"):
387 tar.add("autobuild.log")
388 sys_info = self.write_system_info()
389 tar.add(sys_info)
390 tar.close()
391
392 def remove_logs(self):
393 for b in self.tlist:
394 os.unlink(b.stdout_path)
395 os.unlink(b.stderr_path)
396
397 def start_tail(self):
398 cwd = os.getcwd()
399 cmd = "tail -f *.stdout *.stderr"
400 os.chdir(gitroot)
401 self.tail_proc = Popen(cmd, shell=True)
402 os.chdir(cwd)
403
404
405def cleanup():
406 if options.nocleanup:
407 return
408 print("Cleaning up ....")
409 for d in cleanup_list:
410 run_cmd("rm -rf %s" % d)
411
412
413def find_git_root():
414 '''get to the top of the git repo'''
415 p=os.getcwd()
416 while p != '/':
417 if os.path.isdir(os.path.join(p, ".git")):
418 return p
419 p = os.path.abspath(os.path.join(p, '..'))
420 return None
421
422
423def daemonize(logfile):
424 pid = os.fork()
425 if pid == 0: # Parent
426 os.setsid()
427 pid = os.fork()
428 if pid != 0: # Actual daemon
429 os._exit(0)
430 else: # Grandparent
431 os._exit(0)
432
433 import resource # Resource usage information.
434 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
435 if maxfd == resource.RLIM_INFINITY:
436 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
437 for fd in range(0, maxfd):
438 try:
439 os.close(fd)
440 except OSError:
441 pass
442 os.open(logfile, os.O_RDWR | os.O_CREAT)
443 os.dup2(0, 1)
444 os.dup2(0, 2)
445
446def write_pidfile(fname):
447 '''write a pid file, cleanup on exit'''
448 f = open(fname, mode='w')
449 f.write("%u\n" % os.getpid())
450 f.close()
451
452
453def rebase_tree(rebase_url, rebase_branch = "master"):
454 rebase_remote = "rebaseon"
455 print("Rebasing on %s" % rebase_url)
456 run_cmd("git describe HEAD", show=True, dir=test_master)
457 run_cmd("git remote add -t %s %s %s" %
458 (rebase_branch, rebase_remote, rebase_url),
459 show=True, dir=test_master)
460 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
461 if options.fix_whitespace:
462 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
463 (rebase_remote, rebase_branch),
464 show=True, dir=test_master)
465 else:
466 run_cmd("git rebase --force-rebase %s/%s" %
467 (rebase_remote, rebase_branch),
468 show=True, dir=test_master)
469 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
470 (rebase_remote, rebase_branch),
471 dir=test_master, output=True)
472 if diff == '':
473 print("No differences between HEAD and %s/%s - exiting" %
474 (rebase_remote, rebase_branch))
475 sys.exit(0)
476 run_cmd("git describe %s/%s" %
477 (rebase_remote, rebase_branch),
478 show=True, dir=test_master)
479 run_cmd("git describe HEAD", show=True, dir=test_master)
480 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
481 (rebase_remote, rebase_branch),
482 show=True, dir=test_master)
483
484def push_to(push_url, push_branch = "master"):
485 push_remote = "pushto"
486 print("Pushing to %s" % push_url)
487 if options.mark:
488 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
489 run_cmd("git commit --amend -c HEAD", dir=test_master)
490 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
491 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
492 run_cmd("git remote add -t %s %s %s" %
493 (push_branch, push_remote, push_url),
494 show=True, dir=test_master)
495 run_cmd("git push %s +HEAD:%s" %
496 (push_remote, push_branch),
497 show=True, dir=test_master)
498
499def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
500
501gitroot = find_git_root()
502if gitroot is None:
503 raise Exception("Failed to find git root")
504
505parser = OptionParser()
506parser.add_option("", "--tail", help="show output while running", default=False, action="store_true")
507parser.add_option("", "--keeplogs", help="keep logs", default=False, action="store_true")
508parser.add_option("", "--nocleanup", help="don't remove test tree", default=False, action="store_true")
509parser.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase,
510 default=def_testbase)
511parser.add_option("", "--passcmd", help="command to run on success", default=None)
512parser.add_option("", "--verbose", help="show all commands as they are run",
513 default=False, action="store_true")
514parser.add_option("", "--rebase", help="rebase on the given tree before testing",
515 default=None, type='str')
516parser.add_option("", "--pushto", help="push to a git url on success",
517 default=None, type='str')
518parser.add_option("", "--mark", help="add a Tested-By signoff before pushing",
519 default=False, action="store_true")
520parser.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
521 default=False, action="store_true")
522parser.add_option("", "--retry", help="automatically retry if master changes",
523 default=False, action="store_true")
524parser.add_option("", "--email", help="send email to the given address on failure",
525 type='str', default=None)
526parser.add_option("", "--email-from", help="send email from the given address",
527 type='str', default="autobuild@samba.org")
528parser.add_option("", "--email-server", help="send email via the given server",
529 type='str', default='localhost')
530parser.add_option("", "--always-email", help="always send email, even on success",
531 action="store_true")
532parser.add_option("", "--daemon", help="daemonize after initial setup",
533 action="store_true")
534parser.add_option("", "--branch", help="the branch to work on (default=master)",
535 default="master", type='str')
536parser.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
537 default=gitroot, type='str')
538parser.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
539 default=False, action="store_true")
540
541def send_email(subject, text, log_tar):
542 outer = MIMEMultipart()
543 outer['Subject'] = subject
544 outer['To'] = options.email
545 outer['From'] = options.email_from
546 outer['Date'] = email.utils.formatdate(localtime = True)
547 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
548 outer.attach(MIMEText(text, 'plain'))
549 if options.attach_logs:
550 fp = open(log_tar, 'rb')
551 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
552 fp.close()
553 # Set the filename parameter
554 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
555 outer.attach(msg)
556 content = outer.as_string()
557 s = smtplib.SMTP(options.email_server)
558 s.sendmail(options.email_from, [options.email], content)
559 s.set_debuglevel(1)
560 s.quit()
561
562def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
563 elapsed_time, log_base=None):
564 '''send an email to options.email about the failure'''
565 elapsed_minutes = elapsed_time / 60.0
566 user = os.getenv("USER")
567 if log_base is None:
568 log_base = gitroot
569 text = '''
570Dear Developer,
571
572Your autobuild on %s failed after %.1f minutes
573when trying to test %s with the following error:
574
575 %s
576
577the autobuild has been abandoned. Please fix the error and resubmit.
578
579A summary of the autobuild process is here:
580
581 %s/autobuild.log
582''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
583
584 if failed_task != 'rebase':
585 text += '''
586You can see logs of the failed task here:
587
588 %s/%s.stdout
589 %s/%s.stderr
590
591or you can get full logs of all tasks in this job here:
592
593 %s/logs.tar.gz
594
595The top commit for the tree that was built was:
596
597%s
598
599''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
600
601 logs = os.path.join(gitroot, 'logs.tar.gz')
602 send_email('autobuild failure on %s for task %s during %s'
603 % (platform.node(), failed_task, failed_stage),
604 text, logs)
605
606def email_success(elapsed_time, log_base=None):
607 '''send an email to options.email about a successful build'''
608 user = os.getenv("USER")
609 if log_base is None:
610 log_base = gitroot
611 text = '''
612Dear Developer,
613
614Your autobuild on %s has succeeded after %.1f minutes.
615
616''' % (platform.node(), elapsed_time / 60.)
617
618 if options.keeplogs:
619 text += '''
620
621you can get full logs of all tasks in this job here:
622
623 %s/logs.tar.gz
624
625''' % log_base
626
627 text += '''
628The top commit for the tree that was built was:
629
630%s
631''' % top_commit_msg
632
633 logs = os.path.join(gitroot, 'logs.tar.gz')
634 send_email('autobuild sucess on %s ' % platform.node(),
635 text, logs)
636
637
638(options, args) = parser.parse_args()
639
640if options.retry:
641 if options.rebase is None:
642 raise Exception('You can only use --retry if you also rebase')
643
644testbase = "%s/b%u" % (options.testbase, os.getpid())
645test_master = "%s/master" % testbase
646
647# get the top commit message, for emails
648top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
649
650try:
651 os.makedirs(testbase)
652except Exception, reason:
653 raise Exception("Unable to create %s : %s" % (testbase, reason))
654cleanup_list.append(testbase)
655
656if options.daemon:
657 logfile = os.path.join(testbase, "log")
658 print "Forking into the background, writing progress to %s" % logfile
659 daemonize(logfile)
660
661write_pidfile(gitroot + "/autobuild.pid")
662
663start_time = time.time()
664
665while True:
666 try:
667 run_cmd("rm -rf %s" % test_master)
668 cleanup_list.append(test_master)
669 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
670 except Exception:
671 cleanup()
672 raise
673
674 try:
675 try:
676 if options.rebase is not None:
677 rebase_tree(options.rebase, rebase_branch=options.branch)
678 except Exception:
679 cleanup_list.append(gitroot + "/autobuild.pid")
680 cleanup()
681 elapsed_time = time.time() - start_time
682 email_failure(-1, 'rebase', 'rebase', 'rebase',
683 'rebase on %s failed' % options.branch,
684 elapsed_time, log_base=options.log_base)
685 sys.exit(1)
686 blist = buildlist(tasks, args, options.rebase, rebase_branch=options.branch)
687 if options.tail:
688 blist.start_tail()
689 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
690 if status != 0 or errstr != "retry":
691 break
692 cleanup()
693 except Exception:
694 cleanup()
695 raise
696
697cleanup_list.append(gitroot + "/autobuild.pid")
698
699blist.kill_kids()
700if options.tail:
701 print("waiting for tail to flush")
702 time.sleep(1)
703
704elapsed_time = time.time() - start_time
705if status == 0:
706 print errstr
707 if options.passcmd is not None:
708 print("Running passcmd: %s" % options.passcmd)
709 run_cmd(options.passcmd, dir=test_master)
710 if options.pushto is not None:
711 push_to(options.pushto, push_branch=options.branch)
712 if options.keeplogs or options.attach_logs:
713 blist.tarlogs("logs.tar.gz")
714 print("Logs in logs.tar.gz")
715 if options.always_email:
716 email_success(elapsed_time, log_base=options.log_base)
717 blist.remove_logs()
718 cleanup()
719 print(errstr)
720 sys.exit(0)
721
722# something failed, gather a tar of the logs
723blist.tarlogs("logs.tar.gz")
724
725if options.email is not None:
726 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
727 elapsed_time, log_base=options.log_base)
728else:
729 elapsed_minutes = elapsed_time / 60.0
730 print '''
731
732####################################################################
733
734AUTOBUILD FAILURE
735
736Your autobuild on %s failed after %.1f minutes
737when trying to test %s with the following error:
738
739 %s
740
741the autobuild has been abandoned. Please fix the error and resubmit.
742
743####################################################################
744
745''' % (platform.node(), elapsed_minutes, failed_task, errstr)
746
747cleanup()
748print(errstr)
749print("Logs in logs.tar.gz")
750sys.exit(status)
Note: See TracBrowser for help on using the repository browser.