2 # run tests on all Samba subprojects and push to a git tree on success
3 # Copyright Andrew Tridgell 2010
4 # released under GNU GPL v3 or later
6 from subprocess
import call
, check_call
,Popen
, PIPE
7 import os
, tarfile
, sys
, time
8 from optparse
import OptionParser
11 from email
.mime
.text
import MIMEText
12 from email
.mime
.base
import MIMEBase
13 from email
.mime
.application
import MIMEApplication
14 from email
.mime
.multipart
import MIMEMultipart
15 from distutils
.sysconfig
import get_python_lib
18 # This speeds up testing remarkably.
19 os
.environ
['TDB_NO_FSYNC'] = '1'
33 "talloc" : "lib/talloc",
34 "replace" : "lib/replace",
35 "tevent" : "lib/tevent",
42 defaulttasks
= [ "ctdb", "samba", "samba-xc", "samba-o3", "samba-ctdb", "samba-libs", "samba-static", "ldb", "tdb", "talloc", "replace", "tevent", "pidl" ]
44 samba_configure_params
= " --picky-developer ${PREFIX} ${EXTRA_PYTHON} --with-profiling-data"
46 samba_libs_envvars
= "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH"
47 samba_libs_envvars
+= " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
48 samba_libs_envvars
+= " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
49 samba_libs_configure_base
= samba_libs_envvars
+ " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX} ${EXTRA_PYTHON}"
50 samba_libs_configure_libs
= samba_libs_configure_base
+ " --bundled-libraries=NONE"
51 samba_libs_configure_samba
= samba_libs_configure_base
+ " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent"
53 if os
.environ
.get("AUTOBUILD_NO_EXTRA_PYTHON", "0") == "1":
56 extra_python
= "--extra-python=/usr/bin/python3"
59 "ctdb" : [ ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
60 ("configure", "./configure ${PREFIX}", "text/plain"),
61 ("make", "make all", "text/plain"),
62 ("install", "make install", "text/plain"),
63 ("test", "make autotest", "text/plain"),
64 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
65 ("clean", "make clean", "text/plain") ],
67 # We have 'test' before 'install' because, 'test' should work without 'install'
68 "samba" : [ ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
69 ("make", "make -j", "text/plain"),
70 ("test", "make test FAIL_IMMEDIATELY=1", "text/plain"),
71 ("install", "make install", "text/plain"),
72 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
73 ("clean", "make clean", "text/plain") ],
75 # Test cross-compile infrastructure
76 "samba-xc" : [ ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
77 ("configure-cross-execute", "./configure.developer -b ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
78 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params
, "text/plain"),
79 ("configure-cross-answers", "./configure.developer -b ./bin-xa --cross-compile" \
80 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params
, "text/plain"),
81 ("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")],
83 # test build with -O3 -- catches extra warnings and bugs
84 "samba-o3" : [ ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
85 ("configure", "ADDITIONAL_CFLAGS='-O3' ./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
86 ("make", "make -j", "text/plain"),
87 ("test", "make quicktest FAIL_IMMEDIATELY=1 TESTS='\(ad_dc\)'", "text/plain"),
88 ("install", "make install", "text/plain"),
89 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
90 ("clean", "make clean", "text/plain") ],
92 "samba-ctdb" : [ ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
94 # make sure we have tdb around:
95 ("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"),
96 ("tdb-make", "cd lib/tdb && make", "text/plain"),
97 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
100 # build samba with cluster support (also building ctdb):
101 ("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"),
102 ("samba-make", "make", "text/plain"),
103 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
104 ("samba-install", "make install", "text/plain"),
105 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
108 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
109 ("clean", "make clean", "text/plain"),
110 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain") ],
113 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
114 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs
, "text/plain"),
115 ("talloc-make", "cd lib/talloc && make", "text/plain"),
116 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
118 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs
, "text/plain"),
119 ("tdb-make", "cd lib/tdb && make", "text/plain"),
120 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
122 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs
, "text/plain"),
123 ("tevent-make", "cd lib/tevent && make", "text/plain"),
124 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
126 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs
, "text/plain"),
127 ("ldb-make", "cd lib/ldb && make", "text/plain"),
128 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
130 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
131 ("nondevel-make", "make -j", "text/plain"),
132 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
133 ("nondevel-install", "make install", "text/plain"),
134 ("nondevel-dist", "make dist", "text/plain"),
136 # retry with all modules shared
137 ("allshared-distclean", "make distclean", "text/plain"),
138 ("allshared-configure", samba_libs_configure_samba
+ " --with-shared-modules=ALL", "text/plain"),
139 ("allshared-make", "make -j", "text/plain")],
142 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
143 # build with all modules static
144 ("allstatic-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=ALL", "text/plain"),
145 ("allstatic-make", "make -j", "text/plain"),
147 # retry without any required modules
148 ("none-distclean", "make distclean", "text/plain"),
149 ("none-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
150 ("none-make", "make -j", "text/plain"),
152 # retry with nonshared smbd and smbtorture
153 ("nonshared-distclean", "make distclean", "text/plain"),
154 ("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"),
155 ("nonshared-make", "make -j", "text/plain")],
158 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
159 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
160 ("make", "make", "text/plain"),
161 ("install", "make install", "text/plain"),
162 ("test", "make test", "text/plain"),
163 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
164 ("distcheck", "make distcheck", "text/plain"),
165 ("clean", "make clean", "text/plain") ],
168 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
169 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
170 ("make", "make", "text/plain"),
171 ("install", "make install", "text/plain"),
172 ("test", "make test", "text/plain"),
173 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
174 ("distcheck", "make distcheck", "text/plain"),
175 ("clean", "make clean", "text/plain") ],
178 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
179 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
180 ("make", "make", "text/plain"),
181 ("install", "make install", "text/plain"),
182 ("test", "make test", "text/plain"),
183 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
184 ("distcheck", "make distcheck", "text/plain"),
185 ("clean", "make clean", "text/plain") ],
188 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
189 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
190 ("make", "make", "text/plain"),
191 ("install", "make install", "text/plain"),
192 ("test", "make test", "text/plain"),
193 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
194 ("distcheck", "make distcheck", "text/plain"),
195 ("clean", "make clean", "text/plain") ],
198 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
199 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
200 ("make", "make", "text/plain"),
201 ("install", "make install", "text/plain"),
202 ("test", "make test", "text/plain"),
203 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
204 ("distcheck", "make distcheck", "text/plain"),
205 ("clean", "make clean", "text/plain") ],
208 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
209 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
210 ("touch", "touch *.yp", "text/plain"),
211 ("make", "make", "text/plain"),
212 ("test", "make test", "text/plain"),
213 ("install", "make install", "text/plain"),
214 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
215 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
216 ("clean", "make clean", "text/plain") ],
218 # these are useful for debugging autobuild
219 'pass' : [ ("pass", 'echo passing && /bin/true', "text/plain") ],
220 'fail' : [ ("fail", 'echo failing && /bin/false', "text/plain") ]
223 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
225 show
= options
.verbose
227 print("Running: '%s' in '%s'" % (cmd
, dir))
229 return Popen([cmd
], shell
=True, stdout
=PIPE
, cwd
=dir).communicate()[0]
231 return check_call(cmd
, shell
=True, cwd
=dir)
233 return call(cmd
, shell
=True, cwd
=dir)
236 class builder(object):
237 '''handle build of one directory'''
239 def __init__(self
, name
, sequence
, cp
=True):
241 self
.dir = builddirs
[name
]
243 self
.tag
= self
.name
.replace('/', '_')
244 self
.sequence
= sequence
246 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
247 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
249 print("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
250 print("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
251 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
252 self
.stdout
= open(self
.stdout_path
, 'w')
253 self
.stderr
= open(self
.stderr_path
, 'w')
254 self
.stdin
= open("/dev/null", 'r')
255 self
.sdir
= "%s/%s" % (testbase
, self
.tag
)
256 self
.prefix
= "%s/prefix/%s" % (testbase
, self
.tag
)
257 run_cmd("rm -rf %s" % self
.sdir
)
258 cleanup_list
.append(self
.sdir
)
259 cleanup_list
.append(self
.prefix
)
260 os
.makedirs(self
.sdir
)
261 run_cmd("rm -rf %s" % self
.sdir
)
263 run_cmd("cp --recursive --link --archive %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
265 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
268 def start_next(self
):
269 if self
.next
== len(self
.sequence
):
270 print '%s: Completed OK' % self
.name
273 (self
.stage
, self
.cmd
, self
.output_mime_type
) = self
.sequence
[self
.next
]
274 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib
=1, prefix
=self
.prefix
))
275 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
276 self
.cmd
= self
.cmd
.replace("${EXTRA_PYTHON}", "%s" % extra_python
)
277 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
278 # if self.output_mime_type == "text/x-subunit":
279 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
280 print '%s: [%s] Running %s' % (self
.name
, self
.stage
, self
.cmd
)
282 os
.chdir("%s/%s" % (self
.sdir
, self
.dir))
283 self
.proc
= Popen(self
.cmd
, shell
=True,
284 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
289 class buildlist(object):
290 '''handle build of multiple directories'''
292 def __init__(self
, tasklist
, tasknames
, rebase_url
, rebase_branch
="master"):
295 self
.tail_proc
= None
298 tasknames
= defaulttasks
300 # If we are only running one test,
301 # do not sleep randomly to wait for it to start
302 os
.environ
['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
305 b
= builder(n
, tasks
[n
], cp
=n
is not "pidl")
308 rebase_remote
= "rebaseon"
309 retry_task
= [ ("retry",
311 git remote add -t %s %s %s
315 git describe %s/%s > old_remote_branch.desc
317 git describe %s/%s > remote_branch.desc
318 diff old_remote_branch.desc remote_branch.desc
321 rebase_branch
, rebase_remote
, rebase_url
,
323 rebase_remote
, rebase_branch
,
325 rebase_remote
, rebase_branch
329 self
.retry
= builder('retry', retry_task
, cp
=False)
330 self
.need_retry
= False
333 if self
.tail_proc
is not None:
334 self
.tail_proc
.terminate()
335 self
.tail_proc
.wait()
336 self
.tail_proc
= None
337 if self
.retry
is not None:
338 self
.retry
.proc
.terminate()
339 self
.retry
.proc
.wait()
342 if b
.proc
is not None:
343 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.sdir
, checkfail
=False)
355 b
.status
= b
.proc
.poll()
361 ret
= self
.retry
.proc
.poll()
363 self
.need_retry
= True
373 if options
.retry
and self
.need_retry
:
375 print("retry needed")
376 return (0, None, None, None, "retry")
379 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
381 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
384 return (0, None, None, None, "All OK")
386 def write_system_info(self
):
387 filename
= 'system-info.txt'
388 f
= open(filename
, 'w')
389 for cmd
in ['uname -a', 'free', 'cat /proc/cpuinfo']:
390 print >>f
, '### %s' % cmd
391 print >>f
, run_cmd(cmd
, output
=True, checkfail
=False)
396 def tarlogs(self
, fname
):
397 tar
= tarfile
.open(fname
, "w:gz")
399 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
400 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
401 if os
.path
.exists("autobuild.log"):
402 tar
.add("autobuild.log")
403 sys_info
= self
.write_system_info()
407 def remove_logs(self
):
409 os
.unlink(b
.stdout_path
)
410 os
.unlink(b
.stderr_path
)
412 def start_tail(self
):
414 cmd
= "tail -f *.stdout *.stderr"
416 self
.tail_proc
= Popen(cmd
, shell
=True)
421 if options
.nocleanup
:
423 print("Cleaning up ....")
424 for d
in cleanup_list
:
425 run_cmd("rm -rf %s" % d
)
429 '''get to the top of the git repo'''
432 if os
.path
.isdir(os
.path
.join(p
, ".git")):
434 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
438 def daemonize(logfile
):
440 if pid
== 0: # Parent
443 if pid
!= 0: # Actual daemon
448 import resource
# Resource usage information.
449 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
450 if maxfd
== resource
.RLIM_INFINITY
:
451 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
452 for fd
in range(0, maxfd
):
457 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
461 def write_pidfile(fname
):
462 '''write a pid file, cleanup on exit'''
463 f
= open(fname
, mode
='w')
464 f
.write("%u\n" % os
.getpid())
468 def rebase_tree(rebase_url
, rebase_branch
= "master"):
469 rebase_remote
= "rebaseon"
470 print("Rebasing on %s" % rebase_url
)
471 run_cmd("git describe HEAD", show
=True, dir=test_master
)
472 run_cmd("git remote add -t %s %s %s" %
473 (rebase_branch
, rebase_remote
, rebase_url
),
474 show
=True, dir=test_master
)
475 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
476 if options
.fix_whitespace
:
477 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
478 (rebase_remote
, rebase_branch
),
479 show
=True, dir=test_master
)
481 run_cmd("git rebase --force-rebase %s/%s" %
482 (rebase_remote
, rebase_branch
),
483 show
=True, dir=test_master
)
484 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
485 (rebase_remote
, rebase_branch
),
486 dir=test_master
, output
=True)
488 print("No differences between HEAD and %s/%s - exiting" %
489 (rebase_remote
, rebase_branch
))
491 run_cmd("git describe %s/%s" %
492 (rebase_remote
, rebase_branch
),
493 show
=True, dir=test_master
)
494 run_cmd("git describe HEAD", show
=True, dir=test_master
)
495 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
496 (rebase_remote
, rebase_branch
),
497 show
=True, dir=test_master
)
499 def push_to(push_url
, push_branch
= "master"):
500 push_remote
= "pushto"
501 print("Pushing to %s" % push_url
)
503 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
504 run_cmd("git commit --amend -c HEAD", dir=test_master
)
505 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
506 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
507 run_cmd("git remote add -t %s %s %s" %
508 (push_branch
, push_remote
, push_url
),
509 show
=True, dir=test_master
)
510 run_cmd("git push %s +HEAD:%s" %
511 (push_remote
, push_branch
),
512 show
=True, dir=test_master
)
514 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
516 gitroot
= find_git_root()
518 raise Exception("Failed to find git root")
520 parser
= OptionParser()
521 parser
.add_option("", "--tail", help="show output while running", default
=False, action
="store_true")
522 parser
.add_option("", "--keeplogs", help="keep logs", default
=False, action
="store_true")
523 parser
.add_option("", "--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
524 parser
.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase
,
525 default
=def_testbase
)
526 parser
.add_option("", "--passcmd", help="command to run on success", default
=None)
527 parser
.add_option("", "--verbose", help="show all commands as they are run",
528 default
=False, action
="store_true")
529 parser
.add_option("", "--rebase", help="rebase on the given tree before testing",
530 default
=None, type='str')
531 parser
.add_option("", "--pushto", help="push to a git url on success",
532 default
=None, type='str')
533 parser
.add_option("", "--mark", help="add a Tested-By signoff before pushing",
534 default
=False, action
="store_true")
535 parser
.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
536 default
=False, action
="store_true")
537 parser
.add_option("", "--retry", help="automatically retry if master changes",
538 default
=False, action
="store_true")
539 parser
.add_option("", "--email", help="send email to the given address on failure",
540 type='str', default
=None)
541 parser
.add_option("", "--email-from", help="send email from the given address",
542 type='str', default
="autobuild@samba.org")
543 parser
.add_option("", "--email-server", help="send email via the given server",
544 type='str', default
='localhost')
545 parser
.add_option("", "--always-email", help="always send email, even on success",
547 parser
.add_option("", "--daemon", help="daemonize after initial setup",
549 parser
.add_option("", "--branch", help="the branch to work on (default=master)",
550 default
="master", type='str')
551 parser
.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
552 default
=gitroot
, type='str')
553 parser
.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
554 default
=False, action
="store_true")
556 def send_email(subject
, text
, log_tar
):
557 outer
= MIMEMultipart()
558 outer
['Subject'] = subject
559 outer
['To'] = options
.email
560 outer
['From'] = options
.email_from
561 outer
['Date'] = email
.utils
.formatdate(localtime
= True)
562 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
563 outer
.attach(MIMEText(text
, 'plain'))
564 if options
.attach_logs
:
565 fp
= open(log_tar
, 'rb')
566 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
568 # Set the filename parameter
569 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
571 content
= outer
.as_string()
572 s
= smtplib
.SMTP(options
.email_server
)
573 s
.sendmail(options
.email_from
, [options
.email
], content
)
577 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
578 elapsed_time
, log_base
=None, add_log_tail
=True):
579 '''send an email to options.email about the failure'''
580 elapsed_minutes
= elapsed_time
/ 60.0
581 user
= os
.getenv("USER")
587 Your autobuild on %s failed after %.1f minutes
588 when trying to test %s with the following error:
592 the autobuild has been abandoned. Please fix the error and resubmit.
594 A summary of the autobuild process is here:
597 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
599 if failed_task
!= 'rebase':
601 You can see logs of the failed task here:
606 or you can get full logs of all tasks in this job here:
610 The top commit for the tree that was built was:
614 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
617 f
= open("%s/%s.stdout" % (gitroot
, failed_tag
), 'r')
618 lines
= f
.readlines()
619 log_tail
= "".join(lines
[-50:])
620 num_lines
= len(lines
)
622 # Also include stderr (compile failures) if < 50 lines of stdout
623 f
= open("%s/%s.stderr" % (gitroot
, failed_tag
), 'r')
624 log_tail
+= "".join(f
.readlines()[-(50-num_lines
):])
627 The last 50 lines of log messages:
633 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
634 send_email('autobuild failure on %s for task %s during %s'
635 % (platform
.node(), failed_task
, failed_stage
),
638 def email_success(elapsed_time
, log_base
=None):
639 '''send an email to options.email about a successful build'''
640 user
= os
.getenv("USER")
646 Your autobuild on %s has succeeded after %.1f minutes.
648 ''' % (platform
.node(), elapsed_time
/ 60.)
653 you can get full logs of all tasks in this job here:
660 The top commit for the tree that was built was:
665 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
666 send_email('autobuild success on %s' % platform
.node(),
670 (options
, args
) = parser
.parse_args()
673 if options
.rebase
is None:
674 raise Exception('You can only use --retry if you also rebase')
676 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
677 test_master
= "%s/master" % testbase
679 # get the top commit message, for emails
680 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
683 os
.makedirs(testbase
)
684 except Exception, reason
:
685 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
686 cleanup_list
.append(testbase
)
689 logfile
= os
.path
.join(testbase
, "log")
690 print "Forking into the background, writing progress to %s" % logfile
693 write_pidfile(gitroot
+ "/autobuild.pid")
695 start_time
= time
.time()
699 run_cmd("rm -rf %s" % test_master
)
700 cleanup_list
.append(test_master
)
701 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
708 if options
.rebase
is not None:
709 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
711 cleanup_list
.append(gitroot
+ "/autobuild.pid")
713 elapsed_time
= time
.time() - start_time
714 email_failure(-1, 'rebase', 'rebase', 'rebase',
715 'rebase on %s failed' % options
.branch
,
716 elapsed_time
, log_base
=options
.log_base
)
718 blist
= buildlist(tasks
, args
, options
.rebase
, rebase_branch
=options
.branch
)
721 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
722 if status
!= 0 or errstr
!= "retry":
729 cleanup_list
.append(gitroot
+ "/autobuild.pid")
733 print("waiting for tail to flush")
736 elapsed_time
= time
.time() - start_time
739 if options
.passcmd
is not None:
740 print("Running passcmd: %s" % options
.passcmd
)
741 run_cmd(options
.passcmd
, dir=test_master
)
742 if options
.pushto
is not None:
743 push_to(options
.pushto
, push_branch
=options
.branch
)
744 if options
.keeplogs
or options
.attach_logs
:
745 blist
.tarlogs("logs.tar.gz")
746 print("Logs in logs.tar.gz")
747 if options
.always_email
:
748 email_success(elapsed_time
, log_base
=options
.log_base
)
754 # something failed, gather a tar of the logs
755 blist
.tarlogs("logs.tar.gz")
757 if options
.email
is not None:
758 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
759 elapsed_time
, log_base
=options
.log_base
)
761 elapsed_minutes
= elapsed_time
/ 60.0
764 ####################################################################
768 Your autobuild on %s failed after %.1f minutes
769 when trying to test %s with the following error:
773 the autobuild has been abandoned. Please fix the error and resubmit.
775 ####################################################################
777 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
)
781 print("Logs in logs.tar.gz")