| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- #!/usr/bin/env python2.7
- # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
- from __future__ import absolute_import
- from __future__ import division
- from __future__ import print_function
- from __future__ import unicode_literals
- import argparse
- import commands
- import subprocess
- import sys
- import re
- import os
- import time
- #
- # Simple logger
- #
- class Log:
- def __init__(self, filename):
- self.filename = filename
- self.f = open(self.filename, 'w+', 0)
- def caption(self, str):
- line = "\n##### %s #####\n" % str
- if self.f:
- self.f.write("%s \n" % line)
- else:
- print(line)
- def error(self, str):
- data = "\n\n##### ERROR ##### %s" % str
- if self.f:
- self.f.write("%s \n" % data)
- else:
- print(data)
- def log(self, str):
- if self.f:
- self.f.write("%s \n" % str)
- else:
- print(str)
- #
- # Shell Environment
- #
- class Env(object):
- def __init__(self, logfile, tests):
- self.tests = tests
- self.log = Log(logfile)
- def shell(self, cmd, path=os.getcwd()):
- if path:
- os.chdir(path)
- self.log.log("==== shell session ===========================")
- self.log.log("%s> %s" % (path, cmd))
- status = subprocess.call("cd %s; %s" % (path, cmd), shell=True,
- stdout=self.log.f, stderr=self.log.f)
- self.log.log("status = %s" % status)
- self.log.log("============================================== \n\n")
- return status
- def GetOutput(self, cmd, path=os.getcwd()):
- if path:
- os.chdir(path)
- self.log.log("==== shell session ===========================")
- self.log.log("%s> %s" % (path, cmd))
- status, out = commands.getstatusoutput(cmd)
- self.log.log("status = %s" % status)
- self.log.log("out = %s" % out)
- self.log.log("============================================== \n\n")
- return status, out
- #
- # Pre-commit checker
- #
- class PreCommitChecker(Env):
- def __init__(self, args):
- Env.__init__(self, args.logfile, args.tests)
- self.ignore_failure = args.ignore_failure
- #
- # Get commands for a given job from the determinator file
- #
- def get_commands(self, test):
- status, out = self.GetOutput(
- "RATIO=1 build_tools/rocksdb-lego-determinator %s" % test, ".")
- return status, out
- #
- # Run a specific CI job
- #
- def run_test(self, test):
- self.log.caption("Running test %s locally" % test)
- # get commands for the CI job determinator
- status, cmds = self.get_commands(test)
- if status != 0:
- self.log.error("Error getting commands for test %s" % test)
- return False
- # Parse the JSON to extract the commands to run
- cmds = re.findall("'shell':'([^\']*)'", cmds)
- if len(cmds) == 0:
- self.log.log("No commands found")
- return False
- # Run commands
- for cmd in cmds:
- # Replace J=<..> with the local environment variable
- if "J" in os.environ:
- cmd = cmd.replace("J=1", "J=%s" % os.environ["J"])
- cmd = cmd.replace("make ", "make -j%s " % os.environ["J"])
- # Run the command
- status = self.shell(cmd, ".")
- if status != 0:
- self.log.error("Error running command %s for test %s"
- % (cmd, test))
- return False
- return True
- #
- # Run specified CI jobs
- #
- def run_tests(self):
- if not self.tests:
- self.log.error("Invalid args. Please provide tests")
- return False
- self.print_separator()
- self.print_row("TEST", "RESULT")
- self.print_separator()
- result = True
- for test in self.tests:
- start_time = time.time()
- self.print_test(test)
- result = self.run_test(test)
- elapsed_min = (time.time() - start_time) / 60
- if not result:
- self.log.error("Error running test %s" % test)
- self.print_result("FAIL (%dm)" % elapsed_min)
- if not self.ignore_failure:
- return False
- result = False
- else:
- self.print_result("PASS (%dm)" % elapsed_min)
- self.print_separator()
- return result
- #
- # Print a line
- #
- def print_separator(self):
- print("".ljust(60, "-"))
- #
- # Print two colums
- #
- def print_row(self, c0, c1):
- print("%s%s" % (c0.ljust(40), c1.ljust(20)))
- def print_test(self, test):
- print(test.ljust(40), end="")
- sys.stdout.flush()
- def print_result(self, result):
- print(result.ljust(20))
- #
- # Main
- #
- parser = argparse.ArgumentParser(description='RocksDB pre-commit checker.')
- # --log <logfile>
- parser.add_argument('--logfile', default='/tmp/precommit-check.log',
- help='Log file. Default is /tmp/precommit-check.log')
- # --ignore_failure
- parser.add_argument('--ignore_failure', action='store_true', default=False,
- help='Stop when an error occurs')
- # <test ....>
- parser.add_argument('tests', nargs='+',
- help='CI test(s) to run. e.g: unit punit asan tsan ubsan')
- args = parser.parse_args()
- checker = PreCommitChecker(args)
- print("Please follow log %s" % checker.log.filename)
- if not checker.run_tests():
- print("Error running tests. Please check log file %s"
- % checker.log.filename)
- sys.exit(1)
- sys.exit(0)
|