Anton Shestakov <av6@dwimlabs.net>, Sat, 30 Jul 2016 13:05:59 +0800
checker: check linter config, complain and skip linters with problems early
checker.py
Permissions: -rwxr-xr-x
from __future__ import absolute_import, print_function from argparse import ArgumentParser, FileType from fnmatch import fnmatchcase from shutil import rmtree from subprocess import check_call, check_output, CalledProcessError from tempfile import mkdtemp from candolint.utils import rel, timestamp def run_ignore_codes(fn, args, codes, **kwargs): return fn(args, **kwargs) except CalledProcessError as e: if e.returncode not in codes: def run(args, silent=False, get_output=False, ignore_codes=None, env=None): full_env = os.environ.copy() print('$ ' + ' '.join(args)) result = run_ignore_codes(fn, args, ignore_codes, env=full_env) result = fn(args, env=full_env) print('# C&O error: {}'.format(e)) def run_git(args, **kwargs): env = kwargs.pop('env', {}) env.update({'LC_ALL': 'en_US.UTF-8'}) return run(['git'] + args, env=env, **kwargs) def run_hg(args, **kwargs): env = kwargs.pop('env', {}) env.update({'HGPLAIN': '1'}) return run(['hg'] + args, env=env, **kwargs) def read_linter_config(name, path=rel('linters')): fd = open(os.path.join(path, '{}.yml'.format(name))) print("# C&O couldn't load linter config: {}".format(e)) return yaml.safe_load(fd) print("# C&O couldn't parse linter config: {}".format(e)) print("# C&O config doesn't have 'url' defined.") print("# C&O config doesn't have 'scm' defined.") elif config['scm'] not in ('git', 'hg'): print("# C&O checker doesn't support {} yet.".format(config['scm'])) def combined_env(lenv, penv): def git_clone(url, dest): return run_git(['clone', url, dest]) return run_hg(['clone', url, dest]) def git_checkout(commit): return run_git(['checkout', commit]) return run_hg(['update', commit]) return run_git(['status']) return run_hg(['summary']) template = (r'# C&O commit: %H%n' r'# C&O commit ref names: %D%n' r'# C&O commit date: %ai%n' r'# C&O commit author: %aN%n' r'# C&O commit message: %s%n') return run_git(['show', '-q', '--format=format:' + template], silent=True) template = (r'# C&O commit: {rev}:{node}\n' r'# C&O commit branch: {branch}\n' r'# C&O commit date: {date|isodatesec}\n' r'# C&O commit author: {author|person}\n' r'# C&O commit message: {desc|firstline}\n') return run_hg(['log', '-r', '.', '-T', template], silent=True) def git_files(include, exclude): files = run_git(cmd, silent=True, get_output=True) files = files.splitlines() files = [f for f in files if not fnmatchcase(f, e)] def hg_files(include, exclude): files = run_hg(cmd, silent=True, get_output=True, ignore_codes=(1,)) files = files.splitlines() def execute(tmp, config, args): if not prevalidate(config): print('# C&O task: clone') print('# C&O project URL: {}'.format(config['url'])) if config['scm'] == 'git': if not git_clone(config['url'], source): elif config['scm'] == 'hg': if not hg_clone(config['url'], source): print('$ cd {}'.format(source)) if args.commit is not None: if config['scm'] == 'git': if not git_checkout(args.commit): elif config['scm'] == 'hg': if not hg_update(args.commit): if config['scm'] == 'git': elif config['scm'] == 'hg': if config['scm'] == 'git': elif config['scm'] == 'hg': print('# C&O task: setup') for linter in config.get('linters', []): print("# C&O skipping linter without 'name': {}".format(linter)) if 'include' not in linter: print("# C&O skipping '{}': no 'include' in config".format(name)) print('# C&O linters requested: {}'.format(' '.join(distinct))) if name in linter_config: lc = read_linter_config(name) print("# C&O skipping '{}': no config".format(name)) print("# C&O skipping '{}': no 'exec' in config".format(name)) for item in lc.get('setup', []): env = combined_env(lc.get('env'), linter.get('env')) if not run(lc['exec'] + lc['version'], env=env): print('# C&O linters installed: {}'.format(' '.join(linter_config.keys()))) print('# C&O task: checks') if linter['name'] not in linter_config: if config['scm'] == 'git': files = git_files(linter['include'], linter.get('exclude', [])) elif config['scm'] == 'hg': files = hg_files(linter['include'], linter.get('exclude', [])) lc = linter_config[linter['name']] flags = lc.get('flags', []) + linter.get('flags', []) pf = lc.get('post_flags', []) + linter.get('post_flags', []) codes = lc.get('codes', (1,)) env = combined_env(lc.get('env'), linter.get('env')) if not run(cmd + flags + [f] + pf, ignore_codes=codes, env=env): def wrapper(config, args): print('# C&O job started: {}'.format(timestamp())) tmp = mkdtemp(prefix='candolint.') if not execute(tmp, config, args): print('# C&O job failed') print('# C&O task: cleanup') print('# C&O job finished: {}'.format(timestamp())) print('# C&O job failed (exception not caught)') print('# C&O job finished: {}'.format(timestamp())) parser = ArgumentParser() 'config', type=FileType('r'), help='project configuration file (YAML)') parser.add_argument('commit', nargs='?', help='commit to update to') args = parser.parse_args() config = yaml.safe_load(args.config) if __name__ == '__main__':