2010-10-03 02:50:53 +04:00
#!/usr/bin/env python
2010-10-01 23:28:32 +04:00
# run tests on all Samba subprojects and push to a git tree on success
# Copyright Andrew Tridgell 2010
# Copyright Jelmer Vernooij 2010
# released under GNU GPL v3 or later
2010-10-03 05:56:09 +04:00
from cStringIO import StringIO
2010-10-02 22:32:16 +04:00
import fcntl
2010-10-01 23:55:10 +04:00
from subprocess import call , check_call , Popen , PIPE
2010-10-01 23:28:32 +04:00
import os , tarfile , sys , time
from optparse import OptionParser
import smtplib
2010-10-02 22:32:16 +04:00
sys . path . insert ( 0 , os . path . join ( os . path . dirname ( __file__ ) , " ../selftest " ) )
sys . path . insert ( 0 , os . path . join ( os . path . dirname ( __file__ ) , " ../lib/testtools " ) )
sys . path . insert ( 0 , os . path . join ( os . path . dirname ( __file__ ) , " ../lib/subunit/python " ) )
import subunit
import testtools
import subunithelper
2010-10-03 05:56:09 +04:00
from email . mime . application import MIMEApplication
2010-10-01 23:28:32 +04:00
from email . mime . text import MIMEText
2010-10-03 03:31:11 +04:00
from email . mime . multipart import MIMEMultipart
2010-10-01 23:28:32 +04:00
samba_master = os . getenv ( ' SAMBA_MASTER ' , ' git://git.samba.org/samba.git ' )
samba_master_ssh = os . getenv ( ' SAMBA_MASTER_SSH ' , ' git+ssh://git.samba.org/data/git/samba.git ' )
cleanup_list = [ ]
os . putenv ( ' CC ' , " ccache gcc " )
tasks = {
" source3 " : [ ( " autogen " , " ./autogen.sh " , " text/plain " ) ,
( " configure " , " ./configure.developer $ {PREFIX} " , " text/plain " ) ,
( " make basics " , " make basics " , " text/plain " ) ,
( " make " , " make -j 4 everything " , " text/plain " ) , # don't use too many processes
( " install " , " make install " , " text/plain " ) ,
2010-10-02 00:10:13 +04:00
( " test " , " TDB_NO_FSYNC=1 make subunit-test " , " text/x-subunit " ) ] ,
2010-10-01 23:28:32 +04:00
" source4 " : [ ( " configure " , " ./configure.developer $ {PREFIX} " , " text/plain " ) ,
( " make " , " make -j " , " text/plain " ) ,
( " install " , " make install " , " text/plain " ) ,
2010-10-02 00:10:13 +04:00
( " test " , " TDB_NO_FSYNC=1 make subunit-test " , " text/x-subunit " ) ] ,
2010-10-01 23:28:32 +04:00
" source4/lib/ldb " : [ ( " configure " , " ./configure --enable-developer -C $ {PREFIX} " , " text/plain " ) ,
( " make " , " make -j " , " text/plain " ) ,
( " install " , " make install " , " text/plain " ) ,
( " test " , " make test " , " text/plain " ) ] ,
" lib/tdb " : [ ( " autogen " , " ./autogen-waf.sh " , " text/plain " ) ,
( " configure " , " ./configure --enable-developer -C $ {PREFIX} " , " text/plain " ) ,
( " make " , " make -j " , " text/plain " ) ,
( " install " , " make install " , " text/plain " ) ,
( " test " , " make test " , " text/plain " ) ] ,
" lib/talloc " : [ ( " autogen " , " ./autogen-waf.sh " , " text/plain " ) ,
( " configure " , " ./configure --enable-developer -C $ {PREFIX} " , " text/plain " ) ,
( " make " , " make -j " , " text/plain " ) ,
( " install " , " make install " , " text/plain " ) ,
2010-10-02 22:32:16 +04:00
( " test " , " make test " , " text/x-subunit " ) , ] ,
2010-10-01 23:28:32 +04:00
" lib/replace " : [ ( " autogen " , " ./autogen-waf.sh " , " text/plain " ) ,
( " configure " , " ./configure --enable-developer -C $ {PREFIX} " , " text/plain " ) ,
( " make " , " make -j " , " text/plain " ) ,
( " install " , " make install " , " text/plain " ) ,
( " test " , " make test " , " text/plain " ) , ] ,
" lib/tevent " : [ ( " configure " , " ./configure --enable-developer -C $ {PREFIX} " , " text/plain " ) ,
( " make " , " make -j " , " text/plain " ) ,
( " install " , " make install " , " text/plain " ) ,
( " test " , " make test " , " text/plain " ) , ] ,
}
2010-10-01 23:43:20 +04:00
2010-10-02 04:59:21 +04:00
def run_cmd ( cmd , dir = None , show = None , output = False , checkfail = True , shell = False ) :
2010-10-01 23:28:32 +04:00
if show is None :
show = options . verbose
if show :
print ( " Running: ' %s ' in ' %s ' " % ( cmd , dir ) )
if output :
2010-10-02 04:59:21 +04:00
return Popen ( cmd , stdout = PIPE , cwd = dir , shell = shell ) . communicate ( ) [ 0 ]
2010-10-01 23:28:32 +04:00
elif checkfail :
2010-10-02 04:59:21 +04:00
return check_call ( cmd , cwd = dir , shell = shell )
2010-10-01 23:28:32 +04:00
else :
2010-10-02 04:59:21 +04:00
return call ( cmd , cwd = dir , shell = shell )
2010-10-01 23:28:32 +04:00
2010-10-03 02:50:53 +04:00
def clone_gitroot ( test_master , revision = " HEAD " ) :
run_cmd ( [ " git " , " clone " , " --shared " , gitroot , test_master ] )
if revision != " HEAD " :
run_cmd ( [ " git " , " checkout " , revision ] )
2010-10-05 13:15:56 +04:00
class RetryChecker ( object ) :
""" Check whether it is necessary to retry. """
def __init__ ( self , dir ) :
run_cmd ( [ " git " , " remote " , " add " , " -t " , " master " , " master " , samba_master ] )
run_cmd ( [ " git " , " fetch " , " master " ] )
cmd = ''' set -e
while : ; do
sleep 60
git describe master / master > old_master . desc
git fetch master
git describe master / master > master . desc
diff old_master . desc master . desc
done
'''
self . proc = Popen ( cmd , shell = True , cwd = self . dir )
def poll ( self ) :
return self . proc . poll ( )
def kill ( self ) :
self . proc . terminate ( )
self . proc . wait ( )
self . retry . proc = None
2010-10-02 15:13:37 +04:00
class TreeStageBuilder ( object ) :
""" Handle building of a particular stage for a tree.
"""
2010-10-02 15:26:36 +04:00
def __init__ ( self , tree , name , command , fail_quickly = False ) :
2010-10-02 15:13:37 +04:00
self . tree = tree
self . name = name
self . command = command
self . fail_quickly = fail_quickly
2010-10-05 13:48:16 +04:00
self . exitcode = None
2010-10-02 22:32:16 +04:00
self . stdin = open ( os . devnull , ' r ' )
2010-10-02 15:13:37 +04:00
def start ( self ) :
2010-10-02 15:26:36 +04:00
raise NotImplementedError ( self . start )
2010-10-02 15:13:37 +04:00
def poll ( self ) :
2010-10-05 13:48:16 +04:00
self . exitcode = self . proc . poll ( )
return self . exitcode
2010-10-02 15:13:37 +04:00
def kill ( self ) :
if self . proc is not None :
try :
run_cmd ( [ " killbysubdir " , self . tree . sdir ] , checkfail = False )
except OSError :
# killbysubdir doesn't exist ?
pass
self . proc . terminate ( )
self . proc . wait ( )
self . proc = None
2010-10-02 15:26:36 +04:00
@property
def failure_reason ( self ) :
2010-10-05 13:48:16 +04:00
return " failed ' %s ' with exit code %d " % ( self . command , self . exitcode )
2010-10-02 15:26:36 +04:00
@property
def failed ( self ) :
2010-10-05 13:48:16 +04:00
return ( self . exitcode != 0 )
2010-10-02 15:26:36 +04:00
class PlainTreeStageBuilder ( TreeStageBuilder ) :
def start ( self ) :
2010-10-02 22:32:16 +04:00
print ' %s : [ %s ] Running %s ' % ( self . name , self . name , self . command )
2010-10-02 15:26:36 +04:00
self . proc = Popen ( self . command , shell = True , cwd = self . tree . dir ,
2010-10-02 22:32:16 +04:00
stdout = self . tree . stdout , stderr = self . tree . stderr ,
stdin = self . stdin )
class AbortingTestResult ( subunithelper . TestsuiteEnabledTestResult ) :
def __init__ ( self , stage ) :
super ( AbortingTestResult , self ) . __init__ ( )
self . stage = stage
def addError ( self , test , details = None ) :
self . stage . proc . terminate ( )
def addFailure ( self , test , details = None ) :
self . stage . proc . terminate ( )
2010-10-02 15:26:36 +04:00
class SubunitTreeStageBuilder ( TreeStageBuilder ) :
def __init__ ( self , tree , name , command , fail_quickly = False ) :
2010-10-02 22:32:16 +04:00
super ( SubunitTreeStageBuilder , self ) . __init__ ( tree , name , command ,
fail_quickly )
2010-10-02 15:26:36 +04:00
self . failed_tests = [ ]
2010-10-02 22:32:16 +04:00
self . subunit_path = os . path . join ( gitroot ,
" %s . %s .subunit " % ( self . tree . tag , self . name ) )
self . tree . logfiles . append (
2010-10-03 03:31:11 +04:00
( self . subunit_path , os . path . basename ( self . subunit_path ) ,
" text/x-subunit " ) )
2010-10-02 22:32:16 +04:00
self . subunit = open ( self . subunit_path , ' w ' )
formatter = subunithelper . PlainFormatter ( False , True , { } )
clients = [ formatter , subunit . TestProtocolClient ( self . subunit ) ]
if fail_quickly :
clients . append ( AbortingTestResult ( self ) )
self . subunit_server = subunit . TestProtocolServer (
testtools . MultiTestResult ( * clients ) ,
self . subunit )
self . buffered = " "
2010-10-02 15:26:36 +04:00
def start ( self ) :
2010-10-02 22:32:16 +04:00
print ' %s : [ %s ] Running ' % ( self . tree . name , self . name )
2010-10-02 15:26:36 +04:00
self . proc = Popen ( self . command , shell = True , cwd = self . tree . dir ,
2010-10-02 22:32:16 +04:00
stdout = PIPE , stderr = self . tree . stderr , stdin = self . stdin )
fd = self . proc . stdout . fileno ( )
fl = fcntl . fcntl ( fd , fcntl . F_GETFL )
fcntl . fcntl ( fd , fcntl . F_SETFL , fl | os . O_NONBLOCK )
def poll ( self ) :
try :
data = self . proc . stdout . read ( )
except IOError :
return None
else :
self . buffered + = data
buffered = " "
for l in self . buffered . splitlines ( True ) :
if l [ - 1 ] == " \n " :
self . subunit_server . lineReceived ( l )
else :
buffered + = l
self . buffered = buffered
2010-10-05 13:48:16 +04:00
self . exitcode = self . proc . poll ( )
if self . exitcode is not None :
2010-10-02 22:32:16 +04:00
self . subunit . close ( )
2010-10-05 13:48:16 +04:00
return self . exitcode
2010-10-02 15:26:36 +04:00
2010-10-02 15:13:37 +04:00
class TreeBuilder ( object ) :
2010-10-01 23:28:32 +04:00
''' handle build of one directory '''
2010-10-02 00:10:13 +04:00
def __init__ ( self , name , sequence , fail_quickly = False ) :
2010-10-01 23:28:32 +04:00
self . name = name
2010-10-02 00:10:13 +04:00
self . fail_quickly = fail_quickly
2010-10-01 23:28:32 +04:00
self . tag = self . name . replace ( ' / ' , ' _ ' )
self . sequence = sequence
self . next = 0
2010-10-05 13:48:16 +04:00
self . stages = [ ]
2010-10-02 22:32:16 +04:00
self . stdout_path = os . path . join ( gitroot , " %s .stdout " % ( self . tag , ) )
self . stderr_path = os . path . join ( gitroot , " %s .stderr " % ( self . tag , ) )
2010-10-03 03:31:11 +04:00
self . logfiles = [
( self . stdout_path , os . path . basename ( self . stdout_path ) , " text/plain " ) ,
( self . stderr_path , os . path . basename ( self . stderr_path ) , " text/plain " ) ,
]
2010-10-01 23:28:32 +04:00
if options . verbose :
print ( " stdout for %s in %s " % ( self . name , self . stdout_path ) )
print ( " stderr for %s in %s " % ( self . name , self . stderr_path ) )
2010-10-02 04:59:21 +04:00
if os . path . exists ( self . stdout_path ) :
os . unlink ( self . stdout_path )
if os . path . exists ( self . stderr_path ) :
os . unlink ( self . stderr_path )
2010-10-01 23:28:32 +04:00
self . stdout = open ( self . stdout_path , ' w ' )
self . stderr = open ( self . stderr_path , ' w ' )
2010-10-02 22:32:16 +04:00
self . sdir = os . path . join ( testbase , self . tag )
2010-10-02 15:13:37 +04:00
if name in [ ' pass ' , ' fail ' , ' retry ' ] :
self . dir = self . sdir
else :
self . dir = os . path . join ( self . sdir , self . name )
self . prefix = os . path . join ( testbase , " prefix " , self . tag )
2010-10-02 04:59:21 +04:00
run_cmd ( [ " rm " , " -rf " , self . sdir ] )
2010-10-01 23:28:32 +04:00
cleanup_list . append ( self . sdir )
cleanup_list . append ( self . prefix )
os . makedirs ( self . sdir )
2010-10-02 04:59:21 +04:00
run_cmd ( [ " rm " , " -rf " , self . sdir ] )
2010-10-03 02:50:53 +04:00
clone_gitroot ( self . sdir , revision )
2010-10-01 23:28:32 +04:00
self . start_next ( )
2010-10-05 13:48:16 +04:00
self . exitcode = None
2010-10-01 23:28:32 +04:00
def start_next ( self ) :
if self . next == len ( self . sequence ) :
print ' %s : Completed OK ' % self . name
self . done = True
2010-10-02 22:32:16 +04:00
self . stdout . close ( )
self . stderr . close ( )
2010-10-01 23:28:32 +04:00
return
2010-10-02 15:13:37 +04:00
( stage_name , cmd , output_mime_type ) = self . sequence [ self . next ]
cmd = cmd . replace ( " $ {PREFIX} " , " --prefix= %s " % self . prefix )
2010-10-02 15:26:36 +04:00
if output_mime_type == " text/plain " :
2010-10-05 13:48:16 +04:00
self . current_stage = PlainTreeStageBuilder ( self , stage_name , cmd ,
2010-10-02 22:32:16 +04:00
self . fail_quickly )
2010-10-02 15:26:36 +04:00
elif output_mime_type == " text/x-subunit " :
2010-10-05 13:48:16 +04:00
self . current_stage = SubunitTreeStageBuilder ( self , stage_name , cmd ,
2010-10-02 22:32:16 +04:00
self . fail_quickly )
2010-10-02 15:26:36 +04:00
else :
raise Exception ( " Unknown output mime type %s " % output_mime_type )
2010-10-05 13:48:16 +04:00
self . stages . append ( self . current_stage )
self . current_stage . start ( )
2010-10-01 23:28:32 +04:00
self . next + = 1
2010-10-02 15:13:37 +04:00
def remove_logs ( self ) :
2010-10-03 03:31:11 +04:00
for path , name , mime_type in self . logfiles :
2010-10-02 22:32:16 +04:00
os . unlink ( path )
2010-10-02 15:13:37 +04:00
2010-10-01 21:03:18 +04:00
def poll ( self ) :
2010-10-05 13:48:16 +04:00
self . exitcode = self . current_stage . poll ( )
if self . exitcode is not None :
self . current_stage = None
return self . exitcode
2010-10-01 21:03:18 +04:00
def kill ( self ) :
2010-10-05 13:48:16 +04:00
if self . current_stage is not None :
self . current_stage . kill ( )
self . current_stage = None
2010-10-01 21:03:18 +04:00
@property
def failed ( self ) :
2010-10-05 13:48:16 +04:00
return any ( [ s . failed for s in self . stages ] )
@property
def failed_stage ( self ) :
for s in self . stages :
if s . failed :
return s
return s
2010-10-01 21:03:18 +04:00
@property
def failure_reason ( self ) :
2010-10-05 13:48:16 +04:00
return " %s : [ %s ] %s " % ( self . name , self . failed_stage . name ,
self . failed_stage . failure_reason )
2010-10-01 21:03:18 +04:00
2010-10-01 23:28:32 +04:00
2010-10-01 23:55:10 +04:00
class BuildList ( object ) :
2010-10-01 23:28:32 +04:00
''' handle build of multiple directories '''
def __init__ ( self , tasklist , tasknames ) :
global tasks
self . tlist = [ ]
self . tail_proc = None
self . retry = None
if tasknames == [ ' pass ' ] :
tasks = { ' pass ' : [ ( " pass " , ' /bin/true ' , " text/plain " ) ] }
if tasknames == [ ' fail ' ] :
tasks = { ' fail ' : [ ( " fail " , ' /bin/false ' , " text/plain " ) ] }
if tasknames == [ ] :
tasknames = tasklist
for n in tasknames :
2010-10-02 15:13:37 +04:00
b = TreeBuilder ( n , tasks [ n ] , not options . fail_slowly )
2010-10-01 23:28:32 +04:00
self . tlist . append ( b )
if options . retry :
2010-10-05 13:15:56 +04:00
self . retry = RetryChecker ( self . sdir )
2010-10-01 23:28:32 +04:00
self . need_retry = False
def kill_kids ( self ) :
if self . tail_proc is not None :
self . tail_proc . terminate ( )
self . tail_proc . wait ( )
self . tail_proc = None
if self . retry is not None :
2010-10-05 13:15:56 +04:00
self . retry . kill ( )
2010-10-01 23:28:32 +04:00
for b in self . tlist :
2010-10-01 21:03:18 +04:00
b . kill ( )
2010-10-01 23:28:32 +04:00
def wait_one ( self ) :
while True :
none_running = True
for b in self . tlist :
2010-10-05 13:48:16 +04:00
if b . current_stage is None :
2010-10-01 23:28:32 +04:00
continue
none_running = False
2010-10-01 21:03:18 +04:00
if b . poll ( ) is None :
2010-10-01 23:28:32 +04:00
continue
return b
if options . retry :
2010-10-05 13:15:56 +04:00
ret = self . retry . poll ( )
if ret :
2010-10-01 23:28:32 +04:00
self . need_retry = True
self . retry = None
return None
if none_running :
return None
time . sleep ( 0.1 )
def run ( self ) :
while True :
b = self . wait_one ( )
if options . retry and self . need_retry :
self . kill_kids ( )
print ( " retry needed " )
2010-10-02 15:17:25 +04:00
return ( 0 , None , None , None , " retry " )
2010-10-01 23:28:32 +04:00
if b is None :
break
2010-10-01 21:03:18 +04:00
if b . failed :
2010-10-01 23:28:32 +04:00
self . kill_kids ( )
2010-10-05 13:48:16 +04:00
return ( b . exitcode , b . name , b . failed_stage , b . tag , b . failure_reason )
2010-10-01 23:28:32 +04:00
b . start_next ( )
self . kill_kids ( )
return ( 0 , None , None , None , " All OK " )
2010-10-03 05:56:09 +04:00
def tarlogs ( self , name = None , fileobj = None ) :
tar = tarfile . open ( name = name , fileobj = fileobj , mode = " w:gz " )
2010-10-01 23:28:32 +04:00
for b in self . tlist :
2010-10-03 03:31:11 +04:00
for ( path , name , mime_type ) in b . logfiles :
2010-10-02 22:32:16 +04:00
tar . add ( path , arcname = name )
if os . path . exists ( " autobuild.log " ) :
tar . add ( " autobuild.log " )
2010-10-01 23:28:32 +04:00
tar . close ( )
2010-10-03 05:56:09 +04:00
def attach_logs ( self , outer ) :
f = StringIO ( )
self . tarlogs ( fileobj = f )
msg = MIMEApplication ( f . getvalue ( ) , " x-gzip " )
msg . add_header ( ' Content-Disposition ' , ' attachment ' ,
filename = " logs.tar.gz " )
outer . attach ( msg )
2010-10-03 03:31:11 +04:00
2010-10-01 23:28:32 +04:00
def remove_logs ( self ) :
for b in self . tlist :
2010-10-02 15:13:37 +04:00
b . remove_logs ( )
2010-10-01 23:28:32 +04:00
def start_tail ( self ) :
cmd = " tail -f *.stdout *.stderr "
2010-10-01 20:52:10 +04:00
self . tail_proc = Popen ( cmd , shell = True , cwd = gitroot )
2010-10-01 23:28:32 +04:00
def cleanup ( ) :
if options . nocleanup :
return
print ( " Cleaning up .... " )
for d in cleanup_list :
2010-10-02 04:59:21 +04:00
run_cmd ( [ " rm " , " -rf " , d ] )
2010-10-01 23:28:32 +04:00
2010-10-01 23:34:31 +04:00
def find_git_root ( p ) :
2010-10-01 23:28:32 +04:00
''' get to the top of the git repo '''
while p != ' / ' :
if os . path . isdir ( os . path . join ( p , " .git " ) ) :
return p
p = os . path . abspath ( os . path . join ( p , ' .. ' ) )
return None
def daemonize ( logfile ) :
pid = os . fork ( )
if pid == 0 : # Parent
os . setsid ( )
pid = os . fork ( )
if pid != 0 : # Actual daemon
os . _exit ( 0 )
else : # Grandparent
os . _exit ( 0 )
import resource # Resource usage information.
maxfd = resource . getrlimit ( resource . RLIMIT_NOFILE ) [ 1 ]
if maxfd == resource . RLIM_INFINITY :
maxfd = 1024 # Rough guess at maximum number of open file descriptors.
for fd in range ( 0 , maxfd ) :
try :
os . close ( fd )
except OSError :
pass
os . open ( logfile , os . O_RDWR | os . O_CREAT )
os . dup2 ( 0 , 1 )
os . dup2 ( 0 , 2 )
def rebase_tree ( url ) :
print ( " Rebasing on %s " % url )
2010-10-03 04:16:11 +04:00
run_cmd ( [ " git " , " remote " , " add " , " -t " , " master " , " master " , url ] , show = True ,
dir = test_master )
2010-10-02 04:59:21 +04:00
run_cmd ( [ " git " , " fetch " , " master " ] , show = True , dir = test_master )
2010-10-01 23:28:32 +04:00
if options . fix_whitespace :
2010-10-03 04:16:11 +04:00
run_cmd ( [ " git " , " rebase " , " --whitespace=fix " , " master/master " ] ,
show = True , dir = test_master )
2010-10-01 23:28:32 +04:00
else :
2010-10-02 04:59:21 +04:00
run_cmd ( [ " git " , " rebase " , " master/master " ] , show = True , dir = test_master )
2010-10-03 04:16:11 +04:00
diff = run_cmd ( [ " git " , " --no-pager " , " diff " , " HEAD " , " master/master " ] ,
dir = test_master , output = True )
2010-10-01 23:28:32 +04:00
if diff == ' ' :
print ( " No differences between HEAD and master/master - exiting " )
sys . exit ( 0 )
def push_to ( url ) :
print ( " Pushing to %s " % url )
if options . mark :
2010-10-03 04:16:11 +04:00
run_cmd ( " EDITOR=script/commit_mark.sh git commit --amend -c HEAD " ,
dir = test_master , shell = True )
# the notes method doesn't work yet, as metze hasn't allowed
# refs/notes/* in master
# run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD",
# dir=test_master)
run_cmd ( [ " git " , " remote " , " add " , " -t " , " master " , " pushto " , url ] , show = True ,
dir = test_master )
run_cmd ( [ " git " , " push " , " pushto " , " +HEAD:master " ] , show = True ,
dir = test_master )
2010-10-01 23:28:32 +04:00
def_testbase = os . getenv ( " AUTOBUILD_TESTBASE " , " /memdisk/ %s " % os . getenv ( ' USER ' ) )
parser = OptionParser ( )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --repository " , help = " repository to run tests for " , default = None , type = str )
parser . add_option ( " --revision " , help = " revision to compile if not HEAD " , default = None , type = str )
parser . add_option ( " --tail " , help = " show output while running " , default = False , action = " store_true " )
parser . add_option ( " --keeplogs " , help = " keep logs " , default = False , action = " store_true " )
parser . add_option ( " --nocleanup " , help = " don ' t remove test tree " , default = False , action = " store_true " )
parser . add_option ( " --testbase " , help = " base directory to run tests in (default %s ) " % def_testbase ,
2010-10-01 23:28:32 +04:00
default = def_testbase )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --passcmd " , help = " command to run on success " , default = None )
parser . add_option ( " --verbose " , help = " show all commands as they are run " ,
2010-10-01 23:28:32 +04:00
default = False , action = " store_true " )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --rebase " , help = " rebase on the given tree before testing " ,
2010-10-01 23:28:32 +04:00
default = None , type = ' str ' )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --rebase-master " , help = " rebase on %s before testing " % samba_master ,
2010-10-01 23:28:32 +04:00
default = False , action = ' store_true ' )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --pushto " , help = " push to a git url on success " ,
2010-10-01 23:28:32 +04:00
default = None , type = ' str ' )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --push-master " , help = " push to %s on success " % samba_master_ssh ,
2010-10-01 23:28:32 +04:00
default = False , action = ' store_true ' )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --mark " , help = " add a Tested-By signoff before pushing " ,
2010-10-01 23:28:32 +04:00
default = False , action = " store_true " )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --fix-whitespace " , help = " fix whitespace on rebase " ,
2010-10-01 23:28:32 +04:00
default = False , action = " store_true " )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --retry " , help = " automatically retry if master changes " ,
2010-10-01 23:28:32 +04:00
default = False , action = " store_true " )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --email " , help = " send email to the given address on failure " ,
2010-10-01 23:28:32 +04:00
type = ' str ' , default = None )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --always-email " , help = " always send email, even on success " ,
2010-10-01 23:28:32 +04:00
action = " store_true " )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --daemon " , help = " daemonize after initial setup " ,
2010-10-01 23:28:32 +04:00
action = " store_true " )
2010-10-03 04:16:11 +04:00
parser . add_option ( " --fail-slowly " , help = " continue running tests even after one has already failed " ,
2010-10-02 00:10:13 +04:00
action = " store_true " )
2010-10-01 23:28:32 +04:00
2010-10-05 13:48:16 +04:00
def email_failure ( blist , exitcode , failed_task , failed_stage , failed_tag , errstr ) :
2010-10-01 23:28:32 +04:00
''' send an email to options.email about the failure '''
user = os . getenv ( " USER " )
text = '''
Dear Developer ,
Your autobuild failed when trying to test % s with the following error :
% s
the autobuild has been abandoned . Please fix the error and resubmit .
You can see logs of the failed task here :
http : / / git . samba . org / % s / samba - autobuild / % s . stdout
http : / / git . samba . org / % s / samba - autobuild / % s . stderr
2010-10-02 15:17:25 +04:00
A summary of the autobuild process is here :
http : / / git . samba . org / % s / samba - autobuild / autobuild . log
2010-10-01 23:28:32 +04:00
or you can get full logs of all tasks in this job here :
http : / / git . samba . org / % s / samba - autobuild / logs . tar . gz
2010-10-02 15:17:25 +04:00
The top commit for the tree that was built was :
% s
2010-10-03 02:50:53 +04:00
''' % (failed_task, errstr, user, failed_tag, user, failed_tag, user, user,
get_top_commit_msg ( test_master ) )
2010-10-03 03:31:11 +04:00
msg = MIMEMultipart ( )
2010-10-03 04:16:11 +04:00
msg [ ' Subject ' ] = ' autobuild failure for task %s during %s ' % (
failed_task , failed_stage )
2010-10-01 23:28:32 +04:00
msg [ ' From ' ] = ' autobuild@samba.org '
msg [ ' To ' ] = options . email
2010-10-03 03:31:11 +04:00
main = MIMEText ( text )
msg . attach ( main )
blist . attach_logs ( msg )
2010-10-01 23:28:32 +04:00
s = smtplib . SMTP ( )
s . connect ( )
s . sendmail ( msg [ ' From ' ] , [ msg [ ' To ' ] ] , msg . as_string ( ) )
s . quit ( )
2010-10-03 03:31:11 +04:00
def email_success ( blist ) :
2010-10-01 23:28:32 +04:00
''' send an email to options.email about a successful build '''
user = os . getenv ( " USER " )
text = '''
Dear Developer ,
Your autobuild has succeeded .
'''
if options . keeplogs :
text + = '''
you can get full logs of all tasks in this job here :
http : / / git . samba . org / % s / samba - autobuild / logs . tar . gz
2010-10-02 15:17:25 +04:00
''' % u ser
text + = '''
The top commit for the tree that was built was :
% s
2010-10-03 02:50:53 +04:00
''' % (get_top_commit_msg(test_master),)
2010-10-02 15:17:25 +04:00
2010-10-03 03:31:11 +04:00
msg = MIMEMultipart ( )
2010-10-01 23:28:32 +04:00
msg [ ' Subject ' ] = ' autobuild success '
msg [ ' From ' ] = ' autobuild@samba.org '
msg [ ' To ' ] = options . email
2010-10-03 03:31:11 +04:00
main = MIMEText ( text , ' plain ' )
msg . attach ( main )
blist . attach_logs ( msg )
2010-10-01 23:28:32 +04:00
s = smtplib . SMTP ( )
s . connect ( )
s . sendmail ( msg [ ' From ' ] , [ msg [ ' To ' ] ] , msg . as_string ( ) )
s . quit ( )
( options , args ) = parser . parse_args ( )
if options . retry :
if not options . rebase_master and options . rebase is None :
raise Exception ( ' You can only use --retry if you also rebase ' )
2010-10-02 22:32:16 +04:00
testbase = os . path . join ( options . testbase , " b %u " % ( os . getpid ( ) , ) )
2010-10-01 23:55:10 +04:00
test_master = os . path . join ( testbase , " master " )
2010-10-01 23:28:32 +04:00
2010-10-01 23:34:31 +04:00
if options . repository is not None :
repository = options . repository
else :
repository = os . getcwd ( )
gitroot = find_git_root ( repository )
2010-10-01 23:28:32 +04:00
if gitroot is None :
2010-10-01 23:34:31 +04:00
raise Exception ( " Failed to find git root under %s " % repository )
2010-10-01 23:28:32 +04:00
2010-10-02 15:17:25 +04:00
# get the top commit message, for emails
2010-10-03 02:50:53 +04:00
if options . revision is not None :
revision = options . revision
else :
revision = " HEAD "
def get_top_commit_msg ( reporoot ) :
return run_cmd ( [ " git " , " log " , " -1 " ] , dir = reporoot , output = True )
2010-10-02 15:17:25 +04:00
2010-10-01 23:28:32 +04:00
try :
os . makedirs ( testbase )
except Exception , reason :
raise Exception ( " Unable to create %s : %s " % ( testbase , reason ) )
cleanup_list . append ( testbase )
if options . daemon :
logfile = os . path . join ( testbase , " log " )
print " Forking into the background, writing progress to %s " % logfile
daemonize ( logfile )
while True :
try :
2010-10-02 04:59:21 +04:00
run_cmd ( [ " rm " , " -rf " , test_master ] )
2010-10-01 23:28:32 +04:00
cleanup_list . append ( test_master )
2010-10-03 02:50:53 +04:00
clone_gitroot ( test_master , revision )
2010-10-01 23:28:32 +04:00
except :
cleanup ( )
raise
try :
if options . rebase is not None :
rebase_tree ( options . rebase )
elif options . rebase_master :
rebase_tree ( samba_master )
2010-10-01 23:55:10 +04:00
blist = BuildList ( tasks , args )
2010-10-01 23:28:32 +04:00
if options . tail :
blist . start_tail ( )
2010-10-05 13:48:16 +04:00
( exitcode , failed_task , failed_stage , failed_tag , errstr ) = blist . run ( )
if exitcode != 0 or errstr != " retry " :
2010-10-01 23:28:32 +04:00
break
cleanup ( )
except :
cleanup ( )
raise
blist . kill_kids ( )
if options . tail :
print ( " waiting for tail to flush " )
time . sleep ( 1 )
2010-10-05 13:48:16 +04:00
if exitcode == 0 :
2010-10-01 23:28:32 +04:00
print errstr
if options . passcmd is not None :
print ( " Running passcmd: %s " % options . passcmd )
2010-10-02 04:59:21 +04:00
run_cmd ( options . passcmd , dir = test_master , shell = True )
2010-10-01 23:28:32 +04:00
if options . pushto is not None :
push_to ( options . pushto )
elif options . push_master :
push_to ( samba_master_ssh )
if options . keeplogs :
blist . tarlogs ( " logs.tar.gz " )
print ( " Logs in logs.tar.gz " )
if options . always_email :
2010-10-03 03:31:11 +04:00
email_success ( blist )
2010-10-01 23:28:32 +04:00
blist . remove_logs ( )
cleanup ( )
print ( errstr )
2010-10-01 23:55:10 +04:00
else :
# something failed, gather a tar of the logs
blist . tarlogs ( " logs.tar.gz " )
2010-10-01 23:28:32 +04:00
2010-10-01 23:55:10 +04:00
if options . email is not None :
2010-10-05 13:48:16 +04:00
email_failure ( blist , exitcode , failed_task , failed_stage , failed_tag ,
2010-10-03 04:16:11 +04:00
errstr )
2010-10-01 23:28:32 +04:00
2010-10-01 23:55:10 +04:00
cleanup ( )
print ( errstr )
print ( " Logs in logs.tar.gz " )
2010-10-05 13:48:16 +04:00
sys . exit ( exitcode )