| #!/usr/local/fbcode/gcc-4.9-glibc-2.20-fb/bin/python2.7 |
| |
| 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) |