Anton Shestakov <av6@dwimlabs.net>, Wed, 20 Sep 2017 11:25:26 +0800
viewer: use two columns for info lists on check page for small screens too
Since lists themselves switch to one column on small screens, there's enough
space for both of them side by side.
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)) def read_intree_config(): fd = open('candolint.yml') print("# C&O couldn't load in-tree config: {}".format(e)) return yaml.safe_load(fd) print("# C&O couldn't parse in-tree 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 process_config(lc, pc): 'env': combined(lc.get('env'), pc.get('env')), 'vars': combined(lc.get('vars', {}), pc.get('vars', {})), 'codes': lc.get('codes', (1,)) return [item % c['vars'] for item in items] for key in ('flags', 'post_flags'): c[key] = render(lc.get(key, []) + pc.get(key, [])) c['setup'] = [render(steps) for steps in lc.get('setup', [])] c['exec'] = render(lc['exec']) c['version'] = render(lc['version']) 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') if args.data is not None: intree = read_intree_config() print('# C&O using in-tree config') print('# C&O in-tree config is required while bootstrapping') print('# C&O not using in-tree config') 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)) 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)) installing = [name for name in distinct if name in linter_config] print('# C&O installing linters: {}'.format(' '.join(installing))) if name not in linter_config: lc = process_config(linter_config[name], linter) key = (name, str(lc.get('setup', []))) for item in lc.get('setup', []): if not run(lc['exec'] + lc['version'], env=lc['env']): if name not in installed: print('# C&O linters installed: {}'.format(' '.join(installed))) print('# C&O task: checks') if 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 = process_config(linter_config[name], linter) cmd = lc['exec'] + lc['flags'] + [f] + lc['post_flags'] if not run(cmd, ignore_codes=lc['codes'], env=lc['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() group = parser.add_mutually_exclusive_group(required=True) '--config', metavar='FILE', type=FileType('r'), help='project configuration file (YAML)') '--data', metavar='YAML', help='bootstrapping data as a YAML string') parser.add_argument('--commit', help='a commit to update to') args = parser.parse_args() if args.data is not None: config = yaml.safe_load(args.data) config = yaml.safe_load(args.config) if __name__ == '__main__':