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 if os
.environ
.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
45 defaulttasks
.remove("samba-o3")
47 samba_configure_params
= " --picky-developer ${PREFIX} ${EXTRA_PYTHON} --with-profiling-data"
49 samba_libs_envvars
= "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH"
50 samba_libs_envvars
+= " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
51 samba_libs_envvars
+= " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
52 samba_libs_configure_base
= samba_libs_envvars
+ " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX} ${EXTRA_PYTHON}"
53 samba_libs_configure_libs
= samba_libs_configure_base
+ " --bundled-libraries=NONE"
54 samba_libs_configure_samba
= samba_libs_configure_base
+ " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent"
56 if os
.environ
.get("AUTOBUILD_NO_EXTRA_PYTHON", "0") == "1":
59 extra_python
= "--extra-python=/usr/bin/python3"
62 "ctdb" : [ ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
63 ("configure", "./configure ${PREFIX}", "text/plain"),
64 ("make", "make all", "text/plain"),
65 ("install", "make install", "text/plain"),
66 ("test", "make autotest", "text/plain"),
67 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
68 ("clean", "make clean", "text/plain") ],
70 # We have 'test' before 'install' because, 'test' should work without 'install'
71 "samba" : [ ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
72 ("make", "make -j", "text/plain"),
73 ("test", "make test FAIL_IMMEDIATELY=1", "text/plain"),
74 ("install", "make install", "text/plain"),
75 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
76 ("clean", "make clean", "text/plain") ],
78 # Test cross-compile infrastructure
79 "samba-xc" : [ ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
80 ("configure-cross-execute", "./configure.developer -b ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
81 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params
, "text/plain"),
82 ("configure-cross-answers", "./configure.developer -b ./bin-xa --cross-compile" \
83 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params
, "text/plain"),
84 ("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")],
86 # test build with -O3 -- catches extra warnings and bugs
87 "samba-o3" : [ ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
88 ("configure", "ADDITIONAL_CFLAGS='-O3' ./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
89 ("make", "make -j", "text/plain"),
90 ("test", "make quicktest FAIL_IMMEDIATELY=1 TESTS='\(ad_dc\)'", "text/plain"),
91 ("install", "make install", "text/plain"),
92 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
93 ("clean", "make clean", "text/plain") ],
95 "samba-ctdb" : [ ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
97 # make sure we have tdb around:
98 ("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"),
99 ("tdb-make", "cd lib/tdb && make", "text/plain"),
100 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
103 # build samba with cluster support (also building ctdb):
104 ("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"),
105 ("samba-make", "make", "text/plain"),
106 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
107 ("samba-install", "make install", "text/plain"),
108 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
111 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
112 ("clean", "make clean", "text/plain"),
113 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain") ],
116 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
117 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs
, "text/plain"),
118 ("talloc-make", "cd lib/talloc && make", "text/plain"),
119 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
121 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs
, "text/plain"),
122 ("tdb-make", "cd lib/tdb && make", "text/plain"),
123 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
125 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs
, "text/plain"),
126 ("tevent-make", "cd lib/tevent && make", "text/plain"),
127 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
129 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs
, "text/plain"),
130 ("ldb-make", "cd lib/ldb && make", "text/plain"),
131 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
133 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
134 ("nondevel-make", "make -j", "text/plain"),
135 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
136 ("nondevel-install", "make install", "text/plain"),
137 ("nondevel-dist", "make dist", "text/plain"),
139 # retry with all modules shared
140 ("allshared-distclean", "make distclean", "text/plain"),
141 ("allshared-configure", samba_libs_configure_samba
+ " --with-shared-modules=ALL", "text/plain"),
142 ("allshared-make", "make -j", "text/plain")],
145 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
146 # build with all modules static
147 ("allstatic-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=ALL", "text/plain"),
148 ("allstatic-make", "make -j", "text/plain"),
150 # retry without any required modules
151 ("none-distclean", "make distclean", "text/plain"),
152 ("none-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
153 ("none-make", "make -j", "text/plain"),
155 # retry with nonshared smbd and smbtorture
156 ("nonshared-distclean", "make distclean", "text/plain"),
157 ("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"),
158 ("nonshared-make", "make -j", "text/plain")],
161 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
162 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
163 ("make", "make", "text/plain"),
164 ("install", "make install", "text/plain"),
165 ("test", "make test", "text/plain"),
166 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
167 ("distcheck", "make distcheck", "text/plain"),
168 ("clean", "make clean", "text/plain") ],
171 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
172 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
173 ("make", "make", "text/plain"),
174 ("install", "make install", "text/plain"),
175 ("test", "make test", "text/plain"),
176 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
177 ("distcheck", "make distcheck", "text/plain"),
178 ("clean", "make clean", "text/plain") ],
181 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
182 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
183 ("make", "make", "text/plain"),
184 ("install", "make install", "text/plain"),
185 ("test", "make test", "text/plain"),
186 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
187 ("distcheck", "make distcheck", "text/plain"),
188 ("clean", "make clean", "text/plain") ],
191 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
192 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
193 ("make", "make", "text/plain"),
194 ("install", "make install", "text/plain"),
195 ("test", "make test", "text/plain"),
196 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
197 ("distcheck", "make distcheck", "text/plain"),
198 ("clean", "make clean", "text/plain") ],
201 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
202 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
203 ("make", "make", "text/plain"),
204 ("install", "make install", "text/plain"),
205 ("test", "make test", "text/plain"),
206 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
207 ("distcheck", "make distcheck", "text/plain"),
208 ("clean", "make clean", "text/plain") ],
211 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
212 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
213 ("touch", "touch *.yp", "text/plain"),
214 ("make", "make", "text/plain"),
215 ("test", "make test", "text/plain"),
216 ("install", "make install", "text/plain"),
217 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
218 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
219 ("clean", "make clean", "text/plain") ],
221 # these are useful for debugging autobuild
222 'pass' : [ ("pass", 'echo passing && /bin/true', "text/plain") ],
223 'fail' : [ ("fail", 'echo failing && /bin/false', "text/plain") ]
226 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
228 show
= options
.verbose
230 print("Running: '%s' in '%s'" % (cmd
, dir))
232 return Popen([cmd
], shell
=True, stdout
=PIPE
, cwd
=dir).communicate()[0]
234 return check_call(cmd
, shell
=True, cwd
=dir)
236 return call(cmd
, shell
=True, cwd
=dir)
239 class builder(object):
240 '''handle build of one directory'''
242 def __init__(self
, name
, sequence
, cp
=True):
244 self
.dir = builddirs
[name
]
246 self
.tag
= self
.name
.replace('/', '_')
247 self
.sequence
= sequence
249 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
250 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
252 print("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
253 print("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
254 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
255 self
.stdout
= open(self
.stdout_path
, 'w')
256 self
.stderr
= open(self
.stderr_path
, 'w')
257 self
.stdin
= open("/dev/null", 'r')
258 self
.sdir
= "%s/%s" % (testbase
, self
.tag
)
259 self
.prefix
= "%s/prefix/%s" % (testbase
, self
.tag
)
260 run_cmd("rm -rf %s" % self
.sdir
)
261 cleanup_list
.append(self
.sdir
)
262 cleanup_list
.append(self
.prefix
)
263 os
.makedirs(self
.sdir
)
264 run_cmd("rm -rf %s" % self
.sdir
)
266 run_cmd("cp --recursive --link --archive %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
268 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
271 def start_next(self
):
272 if self
.next
== len(self
.sequence
):
273 print '%s: Completed OK' % self
.name
276 (self
.stage
, self
.cmd
, self
.output_mime_type
) = self
.sequence
[self
.next
]
277 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib
=1, prefix
=self
.prefix
))
278 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
279 self
.cmd
= self
.cmd
.replace("${EXTRA_PYTHON}", "%s" % extra_python
)
280 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
281 # if self.output_mime_type == "text/x-subunit":
282 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
283 print '%s: [%s] Running %s' % (self
.name
, self
.stage
, self
.cmd
)
285 os
.chdir("%s/%s" % (self
.sdir
, self
.dir))
286 self
.proc
= Popen(self
.cmd
, shell
=True,
287 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
292 class buildlist(object):
293 '''handle build of multiple directories'''
295 def __init__(self
, tasklist
, tasknames
, rebase_url
, rebase_branch
="master"):
298 self
.tail_proc
= None
301 tasknames
= defaulttasks
303 # If we are only running one test,
304 # do not sleep randomly to wait for it to start
305 os
.environ
['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
308 b
= builder(n
, tasks
[n
], cp
=n
is not "pidl")
311 rebase_remote
= "rebaseon"
312 retry_task
= [ ("retry",
314 git remote add -t %s %s %s
318 git describe %s/%s > old_remote_branch.desc
320 git describe %s/%s > remote_branch.desc
321 diff old_remote_branch.desc remote_branch.desc
324 rebase_branch
, rebase_remote
, rebase_url
,
326 rebase_remote
, rebase_branch
,
328 rebase_remote
, rebase_branch
332 self
.retry
= builder('retry', retry_task
, cp
=False)
333 self
.need_retry
= False
336 if self
.tail_proc
is not None:
337 self
.tail_proc
.terminate()
338 self
.tail_proc
.wait()
339 self
.tail_proc
= None
340 if self
.retry
is not None:
341 self
.retry
.proc
.terminate()
342 self
.retry
.proc
.wait()
345 if b
.proc
is not None:
346 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.sdir
, checkfail
=False)
358 b
.status
= b
.proc
.poll()
364 ret
= self
.retry
.proc
.poll()
366 self
.need_retry
= True
376 if options
.retry
and self
.need_retry
:
378 print("retry needed")
379 return (0, None, None, None, "retry")
382 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
384 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
387 return (0, None, None, None, "All OK")
389 def write_system_info(self
):
390 filename
= 'system-info.txt'
391 f
= open(filename
, 'w')
392 for cmd
in ['uname -a', 'free', 'cat /proc/cpuinfo']:
393 print >>f
, '### %s' % cmd
394 print >>f
, run_cmd(cmd
, output
=True, checkfail
=False)
399 def tarlogs(self
, fname
):
400 tar
= tarfile
.open(fname
, "w:gz")
402 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
403 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
404 if os
.path
.exists("autobuild.log"):
405 tar
.add("autobuild.log")
406 sys_info
= self
.write_system_info()
410 def remove_logs(self
):
412 os
.unlink(b
.stdout_path
)
413 os
.unlink(b
.stderr_path
)
415 def start_tail(self
):
417 cmd
= "tail -f *.stdout *.stderr"
419 self
.tail_proc
= Popen(cmd
, shell
=True)
424 if options
.nocleanup
:
426 print("Cleaning up ....")
427 for d
in cleanup_list
:
428 run_cmd("rm -rf %s" % d
)
432 '''get to the top of the git repo'''
435 if os
.path
.isdir(os
.path
.join(p
, ".git")):
437 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
441 def daemonize(logfile
):
443 if pid
== 0: # Parent
446 if pid
!= 0: # Actual daemon
451 import resource
# Resource usage information.
452 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
453 if maxfd
== resource
.RLIM_INFINITY
:
454 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
455 for fd
in range(0, maxfd
):
460 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
464 def write_pidfile(fname
):
465 '''write a pid file, cleanup on exit'''
466 f
= open(fname
, mode
='w')
467 f
.write("%u\n" % os
.getpid())
471 def rebase_tree(rebase_url
, rebase_branch
= "master"):
472 rebase_remote
= "rebaseon"
473 print("Rebasing on %s" % rebase_url
)
474 run_cmd("git describe HEAD", show
=True, dir=test_master
)
475 run_cmd("git remote add -t %s %s %s" %
476 (rebase_branch
, rebase_remote
, rebase_url
),
477 show
=True, dir=test_master
)
478 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
479 if options
.fix_whitespace
:
480 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
481 (rebase_remote
, rebase_branch
),
482 show
=True, dir=test_master
)
484 run_cmd("git rebase --force-rebase %s/%s" %
485 (rebase_remote
, rebase_branch
),
486 show
=True, dir=test_master
)
487 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
488 (rebase_remote
, rebase_branch
),
489 dir=test_master
, output
=True)
491 print("No differences between HEAD and %s/%s - exiting" %
492 (rebase_remote
, rebase_branch
))
494 run_cmd("git describe %s/%s" %
495 (rebase_remote
, rebase_branch
),
496 show
=True, dir=test_master
)
497 run_cmd("git describe HEAD", show
=True, dir=test_master
)
498 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
499 (rebase_remote
, rebase_branch
),
500 show
=True, dir=test_master
)
502 def push_to(push_url
, push_branch
= "master"):
503 push_remote
= "pushto"
504 print("Pushing to %s" % push_url
)
506 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
507 run_cmd("git commit --amend -c HEAD", dir=test_master
)
508 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
509 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
510 run_cmd("git remote add -t %s %s %s" %
511 (push_branch
, push_remote
, push_url
),
512 show
=True, dir=test_master
)
513 run_cmd("git push %s +HEAD:%s" %
514 (push_remote
, push_branch
),
515 show
=True, dir=test_master
)
517 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
519 gitroot
= find_git_root()
521 raise Exception("Failed to find git root")
523 parser
= OptionParser()
524 parser
.add_option("", "--tail", help="show output while running", default
=False, action
="store_true")
525 parser
.add_option("", "--keeplogs", help="keep logs", default
=False, action
="store_true")
526 parser
.add_option("", "--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
527 parser
.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase
,
528 default
=def_testbase
)
529 parser
.add_option("", "--passcmd", help="command to run on success", default
=None)
530 parser
.add_option("", "--verbose", help="show all commands as they are run",
531 default
=False, action
="store_true")
532 parser
.add_option("", "--rebase", help="rebase on the given tree before testing",
533 default
=None, type='str')
534 parser
.add_option("", "--pushto", help="push to a git url on success",
535 default
=None, type='str')
536 parser
.add_option("", "--mark", help="add a Tested-By signoff before pushing",
537 default
=False, action
="store_true")
538 parser
.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
539 default
=False, action
="store_true")
540 parser
.add_option("", "--retry", help="automatically retry if master changes",
541 default
=False, action
="store_true")
542 parser
.add_option("", "--email", help="send email to the given address on failure",
543 type='str', default
=None)
544 parser
.add_option("", "--email-from", help="send email from the given address",
545 type='str', default
="autobuild@samba.org")
546 parser
.add_option("", "--email-server", help="send email via the given server",
547 type='str', default
='localhost')
548 parser
.add_option("", "--always-email", help="always send email, even on success",
550 parser
.add_option("", "--daemon", help="daemonize after initial setup",
552 parser
.add_option("", "--branch", help="the branch to work on (default=master)",
553 default
="master", type='str')
554 parser
.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
555 default
=gitroot
, type='str')
556 parser
.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
557 default
=False, action
="store_true")
559 def send_email(subject
, text
, log_tar
):
560 outer
= MIMEMultipart()
561 outer
['Subject'] = subject
562 outer
['To'] = options
.email
563 outer
['From'] = options
.email_from
564 outer
['Date'] = email
.utils
.formatdate(localtime
= True)
565 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
566 outer
.attach(MIMEText(text
, 'plain'))
567 if options
.attach_logs
:
568 fp
= open(log_tar
, 'rb')
569 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
571 # Set the filename parameter
572 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
574 content
= outer
.as_string()
575 s
= smtplib
.SMTP(options
.email_server
)
576 s
.sendmail(options
.email_from
, [options
.email
], content
)
580 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
581 elapsed_time
, log_base
=None, add_log_tail
=True):
582 '''send an email to options.email about the failure'''
583 elapsed_minutes
= elapsed_time
/ 60.0
584 user
= os
.getenv("USER")
590 Your autobuild on %s failed after %.1f minutes
591 when trying to test %s with the following error:
595 the autobuild has been abandoned. Please fix the error and resubmit.
597 A summary of the autobuild process is here:
600 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
602 if failed_task
!= 'rebase':
604 You can see logs of the failed task here:
609 or you can get full logs of all tasks in this job here:
613 The top commit for the tree that was built was:
617 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
620 f
= open("%s/%s.stdout" % (gitroot
, failed_tag
), 'r')
621 lines
= f
.readlines()
622 log_tail
= "".join(lines
[-50:])
623 num_lines
= len(lines
)
625 # Also include stderr (compile failures) if < 50 lines of stdout
626 f
= open("%s/%s.stderr" % (gitroot
, failed_tag
), 'r')
627 log_tail
+= "".join(f
.readlines()[-(50-num_lines
):])
630 The last 50 lines of log messages:
636 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
637 send_email('autobuild[%s] failure on %s for task %s during %s'
638 % (options
.branch
, platform
.node(), failed_task
, failed_stage
),
641 def email_success(elapsed_time
, log_base
=None):
642 '''send an email to options.email about a successful build'''
643 user
= os
.getenv("USER")
649 Your autobuild on %s has succeeded after %.1f minutes.
651 ''' % (platform
.node(), elapsed_time
/ 60.)
656 you can get full logs of all tasks in this job here:
663 The top commit for the tree that was built was:
668 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
669 send_email('autobuild[%s] success on %s' % (options
.branch
, platform
.node()),
673 (options
, args
) = parser
.parse_args()
676 if options
.rebase
is None:
677 raise Exception('You can only use --retry if you also rebase')
679 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
680 test_master
= "%s/master" % testbase
682 # get the top commit message, for emails
683 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
686 os
.makedirs(testbase
)
687 except Exception, reason
:
688 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
689 cleanup_list
.append(testbase
)
692 logfile
= os
.path
.join(testbase
, "log")
693 print "Forking into the background, writing progress to %s" % logfile
696 write_pidfile(gitroot
+ "/autobuild.pid")
698 start_time
= time
.time()
702 run_cmd("rm -rf %s" % test_master
)
703 cleanup_list
.append(test_master
)
704 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
711 if options
.rebase
is not None:
712 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
714 cleanup_list
.append(gitroot
+ "/autobuild.pid")
716 elapsed_time
= time
.time() - start_time
717 email_failure(-1, 'rebase', 'rebase', 'rebase',
718 'rebase on %s failed' % options
.branch
,
719 elapsed_time
, log_base
=options
.log_base
)
721 blist
= buildlist(tasks
, args
, options
.rebase
, rebase_branch
=options
.branch
)
724 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
725 if status
!= 0 or errstr
!= "retry":
732 cleanup_list
.append(gitroot
+ "/autobuild.pid")
736 print("waiting for tail to flush")
739 elapsed_time
= time
.time() - start_time
742 if options
.passcmd
is not None:
743 print("Running passcmd: %s" % options
.passcmd
)
744 run_cmd(options
.passcmd
, dir=test_master
)
745 if options
.pushto
is not None:
746 push_to(options
.pushto
, push_branch
=options
.branch
)
747 if options
.keeplogs
or options
.attach_logs
:
748 blist
.tarlogs("logs.tar.gz")
749 print("Logs in logs.tar.gz")
750 if options
.always_email
:
751 email_success(elapsed_time
, log_base
=options
.log_base
)
757 # something failed, gather a tar of the logs
758 blist
.tarlogs("logs.tar.gz")
760 if options
.email
is not None:
761 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
762 elapsed_time
, log_base
=options
.log_base
)
764 elapsed_minutes
= elapsed_time
/ 60.0
767 ####################################################################
771 Your autobuild[%s] on %s failed after %.1f minutes
772 when trying to test %s with the following error:
776 the autobuild has been abandoned. Please fix the error and resubmit.
778 ####################################################################
780 ''' % (options
.branch
, platform
.node(), elapsed_minutes
, failed_task
, errstr
)
784 print("Logs in logs.tar.gz")