script: improvements to bisect-test.py
[kai/samba-autobuild/.git] / script / bisect-test.py
1 #!/usr/bin/env python
2 # use git bisect to work out what commit caused a test failure
3 # Copyright Andrew Tridgell 2010
4 # released under GNU GPL v3 or later
5
6
7 from subprocess import call, check_call, Popen, PIPE
8 import os, tempfile, sys
9 from optparse import OptionParser
10
11 parser = OptionParser()
12 parser.add_option("", "--tests", help="list of tests to run", default='*')
13 parser.add_option("", "--good", help="known good revision (default HEAD~100)", default='HEAD~100')
14 parser.add_option("", "--bad", help="known bad revision (default HEAD)", default='HEAD')
15 parser.add_option("", "--skip-build-errors", help="skip revision where make fails",
16                   action='store_true', default=False)
17 parser.add_option("", "--autogen", help="run autogen before each build",action="store_true", default=False)
18 parser.add_option("", "--autogen-command", help="command to use for autogen (default ./autogen.sh)",
19                   type='str', default="./autogen.sh")
20 parser.add_option("", "--configure", help="run configure.developer before each build",
21     action="store_true", default=False)
22 parser.add_option("", "--configure-command", help="the command for configure (default ./configure.developer)",
23                   type='str', default="./configure.developer")
24 parser.add_option("", "--build-command", help="the command to build the tree (default 'make -j')",
25                   type='str', default="make -j")
26 parser.add_option("", "--test-command", help="the command to test the tree (default 'make test')",
27                   type='str', default="make test")
28 parser.add_option("", "--clean", help="run make clean before each build",
29                   action="store_true", default=False)
30
31
32 (opts, args) = parser.parse_args()
33
34
35 def run_cmd(cmd, dir=".", show=True, output=False, checkfail=True):
36     if show:
37         print("Running: '%s' in '%s'" % (cmd, dir))
38     if output:
39         return Popen([cmd], shell=True, stdout=PIPE, cwd=dir).communicate()[0]
40     elif checkfail:
41         return check_call(cmd, shell=True, cwd=dir)
42     else:
43         return call(cmd, shell=True, cwd=dir)
44
45 def find_git_root():
46     '''get to the top of the git repo'''
47     p=os.getcwd()
48     while p != '/':
49         if os.path.isdir(os.path.join(p, ".git")):
50             return p
51         p = os.path.abspath(os.path.join(p, '..'))
52     return None
53
54 cwd = os.getcwd()
55 gitroot = find_git_root()
56
57 # create a bisect script
58 f = tempfile.NamedTemporaryFile(delete=False)
59 f.write("set -x\n")
60 f.write("cd %s || exit 125\n" % cwd)
61 if opts.autogen:
62     f.write("%s || exit 125\n" % opts.autogen_command)
63 if opts.configure:
64     f.write("%s || exit 125\n" % opts.configure_command)
65 if opts.clean:
66     f.write("make clean || exit 125\n")
67 if opts.skip_build_errors:
68     build_err = 125
69 else:
70     build_err = 1
71 f.write("%s || exit %u\n" % (opts.build_command, build_err))
72 f.write("%s || exit 1\n" % opts.test_command)
73 f.write("exit 0\n")
74 f.close()
75
76 def cleanup():
77     run_cmd("git bisect reset", dir=gitroot)
78     os.unlink(f.name)
79     sys.exit(-1)
80
81 # run bisect
82 ret = -1
83 try:
84     run_cmd("git bisect reset", dir=gitroot, show=False, checkfail=False)
85     run_cmd("git bisect start %s %s --" % (opts.bad, opts.good), dir=gitroot)
86     ret = run_cmd("git bisect run bash %s" % f.name, dir=gitroot, show=True, checkfail=False)
87 except KeyboardInterrupt:
88     print("Cleaning up")
89     cleanup()
90 except Exception, reason:
91     print("Failed bisect: %s" % reason)
92     cleanup()
93
94 run_cmd("git bisect reset", dir=gitroot)
95 os.unlink(f.name)
96 sys.exit(ret)