github.phpd.cn/thought-machine/please@v12.2.0+incompatible/tools/misc/ci_lint.py (about) 1 #!/usr/bin/env python3 2 """Implements a bunch of linters on the repo's source. 3 4 Some specific things are run as plz tests (e.g. lint_builtin_rules_test) 5 but this covers more nebulous things like linters as part of the CI build. 6 7 Should be run from the repo root. 8 """ 9 10 import os 11 import subprocess 12 import sys 13 from concurrent import futures 14 from itertools import groupby 15 16 17 # Dir names that are always blacklisted. 18 BLACKLISTED_DIRS = {'plz-out', 'test', 'test_data', 'third_party'} 19 # Count of packages linted so far and total to do 20 done = 0 21 total = 0 22 23 24 class Linter: 25 26 def __init__(self, root): 27 all_go_files = [(d, f) for d, f in self.find_go_files(root)] 28 self.by_dir = [[os.path.join(d, f[1]) for f in files] 29 for d, files in groupby(all_go_files, key=lambda x: x[0])] 30 self.done = 0 31 self.write('Linting... 0 / %d done', len(self.by_dir)) 32 33 def write(self, msg, args, overwrite=False): 34 if sys.stderr.isatty() and not os.environ.get('CI'): 35 if overwrite: 36 sys.stderr.write('\033[1G') 37 sys.stderr.write(msg % args) 38 sys.stderr.flush() 39 40 def reset_line(self): 41 if sys.stderr.isatty() and not os.environ.get('CI'): 42 sys.stderr.write('\033[1G') 43 44 def find_go_files(self, root): 45 for dirname, dirs, files in os.walk(root): 46 # Excludes github.com, gopkg.in, etc. 47 dirs[:] = [d for d in dirs if '.' not in d and d not in BLACKLISTED_DIRS] 48 yield from [(dirname[2:], file) for file in files 49 if file.endswith('.go') and not file.endswith('.bindata.go')] 50 51 def run_linters(self, files): 52 try: 53 # There are two cases of "possible misuse of unsafe.Pointer" in the parser. 54 # We *think* our usage is legit although of course it would be nice to fix 55 # the warnings regardless. For now we disable it to get linting of the rest 56 # of the package. 57 subprocess.check_call(['go', 'tool', 'vet', '-unsafeptr=false', '-structtags=false'] + files) 58 subprocess.check_call(['golint', '-set_exit_status'] + files) 59 return True 60 except subprocess.CalledProcessError: 61 return False 62 finally: 63 self.done += 1 64 self.write('Linting... %d / %d done', (self.done, len(self.by_dir)), overwrite=True) 65 66 67 def main(): 68 linter = Linter('.') 69 with futures.ThreadPoolExecutor(max_workers=6) as executor: 70 if not all(executor.map(linter.run_linters, linter.by_dir)): 71 linter.reset_line() 72 sys.exit('Some linters failed') 73 linter.reset_line() 74 75 76 if __name__ == '__main__': 77 main()