386 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
			
		
		
	
	
			386 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
| from hy.core.language import filter, is_integer, map, reduce
 | |
| import ConfigParser
 | |
| import os
 | |
| import subprocess
 | |
| import operator
 | |
| import re
 | |
| import gettext
 | |
| import sys
 | |
| import getopt
 | |
| sys.path.append(u'Users/ksternberg/build/git-lint/git_lint_src')
 | |
| from git_lint_options import hyopt
 | |
| from git_lint_config import get_config
 | |
| _ = gettext.gettext
 | |
| VERSION = u'0.0.2'
 | |
| 
 | |
| def tap(a):
 | |
|     print(u'TAP:', a)
 | |
|     return a
 | |
| optlist = [[u'o', u'only', True, _(u'A comma-separated list of only those linters to run'), [u'exclude']], [u'x', u'exclude', True, _(u'A comma-separated list of linters to skip'), []], [u'l', u'linters', False, _(u'Show the list of configured linters')], [u'b', u'base', False, _(u'Check all changed files in the repository, not just those in the current directory.'), []], [u'a', u'all', False, _(u'Scan all files in the repository, not just those that have changed.')], [u'e', u'every', False, _(u'Short for -b -a: scan everything')], [u'w', u'workspace', False, _(u'Scan the workspace'), [u'staging']], [u's', u'staging', False, _(u'Scan the staging area (useful for pre-commit).'), []], [u'g', u'changes', False, _(u"Report lint failures only for diff'd sections"), [u'complete']], [u'p', u'complete', False, _(u'Report lint failures for all files'), []], [u'c', u'config', True, _(u'Path to config file'), []], [u'h', u'help', False, _(u'This help message'), []], [u'v', u'version', False, _(u'Version information'), []]]
 | |
| 
 | |
| def get_git_response_raw(cmd):
 | |
| 
 | |
|     def _hy_anon_fn_2():
 | |
|         fullcmd = ([u'git'] + cmd)
 | |
|         process = subprocess.Popen(fullcmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 | |
|         (out, err) = process.communicate()
 | |
|         (out, err)
 | |
|         return (out, err, process.returncode)
 | |
|     return _hy_anon_fn_2()
 | |
| 
 | |
| def get_git_response(cmd):
 | |
| 
 | |
|     def _hy_anon_fn_4():
 | |
|         (out, error, returncode) = get_git_response_raw(cmd)
 | |
|         (out, error, returncode)
 | |
|         return out
 | |
|     return _hy_anon_fn_4()
 | |
| 
 | |
| def split_git_response(cmd):
 | |
| 
 | |
|     def _hy_anon_fn_6():
 | |
|         (out, error, returncode) = get_git_response_raw(cmd)
 | |
|         (out, error, returncode)
 | |
|         return out.splitlines()
 | |
|     return _hy_anon_fn_6()
 | |
| 
 | |
| def split_git_response(cmd):
 | |
| 
 | |
|     def _hy_anon_fn_8():
 | |
|         (out, error, returncode) = get_git_response_raw(cmd)
 | |
|         (out, error, returncode)
 | |
|         return out.splitlines()
 | |
|     return _hy_anon_fn_8()
 | |
| 
 | |
| def run_git_command(cmd):
 | |
| 
 | |
|     def _hy_anon_fn_10():
 | |
|         fullcmd = ([u'git'] + cmd)
 | |
|         return subprocess.call(fullcmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 | |
|     return _hy_anon_fn_10()
 | |
| 
 | |
| def get_shell_response(fullcmd):
 | |
| 
 | |
|     def _hy_anon_fn_12():
 | |
|         process = subprocess.Popen(fullcmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
 | |
|         (out, err) = process.communicate()
 | |
|         (out, err)
 | |
|         return (out, err, process.returncode)
 | |
|     return _hy_anon_fn_12()
 | |
| 
 | |
| def _hy_anon_fn_14():
 | |
|     (out, error, returncode) = get_git_response_raw([u'rev-parse', u'--show-toplevel'])
 | |
|     (out, error, returncode)
 | |
|     return (None if (not (returncode == 0L)) else out.rstrip())
 | |
| git_base = _hy_anon_fn_14()
 | |
| 
 | |
| def get_all_from_cwd():
 | |
|     return split_git_response([u'ls-tree', u'--name-only', u'-r', u'HEAD'])
 | |
| 
 | |
| def get_all_from_base():
 | |
|     return split_git_response([u'ls-tree', u'--name-only', u'-r', u'--full-tree', u'HEAD'])
 | |
| MERGE_CONFLICT_PAIRS = set([u'DD', u'DU', u'AU', u'AA', u'UD', u'UA', u'UU'])
 | |
| 
 | |
| def get_changed_from_source(trackings):
 | |
| 
 | |
|     def _hy_anon_fn_18():
 | |
| 
 | |
|         def _hy_anon_fn_17(t):
 | |
|             return t([0:2], in, MERGE_CONFLICT_PAIRS)
 | |
|         conflicts = filter(_hy_anon_fn_17, trackings)
 | |
|         return (sys.exit(_(u'Current repository contains merge conflicts. Linters will not be run.')) if len(conflicts) else trackings)
 | |
|     return _hy_anon_fn_18()
 | |
| 
 | |
| def get_porcelain_status(cmd):
 | |
| 
 | |
|     def _hy_anon_fn_22():
 | |
|         stream = get_git_response(cmd).split(u'\x00')
 | |
| 
 | |
|         def parse_stream(acc, stream):
 | |
|             if (0L == len(stream)):
 | |
|                 _hy_anon_var_1 = acc
 | |
|             else:
 | |
| 
 | |
|                 def _hy_anon_fn_20():
 | |
|                     temp = stream.pop(0L)
 | |
|                     index = temp.pop(0L)
 | |
|                     workspace = temp.pop(0L)
 | |
|                     filename = temp[1L:]
 | |
|                     (stream.pop(0L) if (index == u'R') else None)
 | |
|                     return parse_stream(acc.append((index, workspace, filename)), stream)
 | |
|                 _hy_anon_var_1 = _hy_anon_fn_20()
 | |
|             return _hy_anon_var_1
 | |
|         return parse_stream([], stream)
 | |
|     return _hy_anon_fn_22()
 | |
| 
 | |
| def modified_in_workspace(s):
 | |
|     return s([0L], in, [u'M', u'A', u'?'])
 | |
| 
 | |
| def modified_in_staging(s):
 | |
|     return s([1L], in, [u'M', u'A', u'?'])
 | |
| 
 | |
| def get_name(s):
 | |
|     return s([2L])
 | |
| 
 | |
| def get_changed_from_base():
 | |
|     return get_changed_from_source(split_git_response([u'status', u'--porcelain', u'--untracked-files=all', git_base]))
 | |
| 
 | |
| def get_staged_from_cwd():
 | |
|     return []
 | |
| 
 | |
| def gen_staged_from_base():
 | |
|     return []
 | |
| 
 | |
| def make_match_filter_matcher(extensions):
 | |
| 
 | |
|     def _hy_anon_fn_30(s):
 | |
|         return re.compile(s, re.I)
 | |
| 
 | |
|     def _hy_anon_fn_31(s):
 | |
|         return ((u'\\.(' + s) + u')$')
 | |
| 
 | |
|     def _hy_anon_fn_32(s):
 | |
|         return re.sub(u'^\\.', u'', s)
 | |
| 
 | |
|     def _hy_anon_fn_33(s):
 | |
|         return (not (0L == len(s)))
 | |
| 
 | |
|     def _hy_anon_fn_34(s):
 | |
|         return s.strip()
 | |
| 
 | |
|     def _hy_anon_fn_35(s):
 | |
|         return s.split(u',')
 | |
|     return _hy_anon_fn_30(_hy_anon_fn_31(u'|'.join(map(_hy_anon_fn_32, filter(_hy_anon_fn_33, set(map(_hy_anon_fn_34, reduce(operator.add, map(_hy_anon_fn_35, extensions)))))))))
 | |
| 
 | |
| def make_match_filter(config):
 | |
| 
 | |
|     def _hy_anon_fn_39():
 | |
| 
 | |
|         def _hy_anon_fn_37(v):
 | |
|             return v.get(u'match', u'')
 | |
|         matcher = make_match_filter_matcher(map(_hy_anon_fn_37, config.itervalues()))
 | |
| 
 | |
|         def _hy_anon_fn_38(path):
 | |
|             return matcher.search(path)
 | |
|         return _hy_anon_fn_38
 | |
|     return _hy_anon_fn_39()
 | |
| 
 | |
| def executable_exists(script, label):
 | |
|     if (not len(script)):
 | |
|         _hy_anon_var_4 = sys.exit(_(u'Syntax error in command configuration for {} ').format(label))
 | |
|     else:
 | |
| 
 | |
|         def _hy_anon_fn_44():
 | |
|             scriptname = script.split(u' ')[0L]
 | |
|             paths = os.environ.get(u'PATH').split(u':')
 | |
| 
 | |
|             def isexecutable(p):
 | |
|                 return (os.path.exists(p) and os.access(p, os.X_OK))
 | |
|             if (not len(scriptname)):
 | |
|                 _hy_anon_var_3 = sys.exit(_(u'Syntax error in command configuration for {} ').format(label))
 | |
|             else:
 | |
|                 if (scriptname[0L] == u'/'):
 | |
|                     _hy_anon_var_2 = (scriptname if isexecutable(scriptname) else None)
 | |
|                 else:
 | |
| 
 | |
|                     def _hy_anon_fn_43():
 | |
| 
 | |
|                         def _hy_anon_fn_42(path):
 | |
|                             return isexecutable(os.path.join(path, scriptname))
 | |
|                         possibles = list(filter(_hy_anon_fn_42, paths))
 | |
|                         return (possibles[0L] if len(possibles) else None)
 | |
|                     _hy_anon_var_2 = _hy_anon_fn_43()
 | |
|                 _hy_anon_var_3 = _hy_anon_var_2
 | |
|             return _hy_anon_var_3
 | |
|         _hy_anon_var_4 = _hy_anon_fn_44()
 | |
|     return _hy_anon_var_4
 | |
| 
 | |
| def print_linters(config):
 | |
|     print(_(u'Currently supported linters:'))
 | |
|     for (linter, items) in config.iteritems():
 | |
|         (print(u'{:<14} {}'.format(linter, _(u'(WARNING: executable not found)'))) if (not executable_exists(items.get(u'command', u''), linter)) else print(u'{:<14} {}'.format(linter, items.get(u'comment', u''))))
 | |
| 
 | |
| def get_porcelain_status():
 | |
| 
 | |
|     def _hy_anon_fn_50():
 | |
|         cmd = [u'status', u'-z', u'--porcelain', u'--untracked-files=all', u'--ignore-submodules=all']
 | |
| 
 | |
|         def nonnull(s):
 | |
|             return (len(s) > 0L)
 | |
|         stream = tap(list(filter(nonnull, get_git_response(cmd).split(u'\x00'))))
 | |
| 
 | |
|         def parse_stream(acc, stream):
 | |
|             if (0L == len(stream)):
 | |
|                 _hy_anon_var_5 = acc
 | |
|             else:
 | |
| 
 | |
|                 def _hy_anon_fn_48():
 | |
|                     temp = stream.pop(0L)
 | |
|                     index = temp[0L]
 | |
|                     workspace = temp[1L]
 | |
|                     filename = tap(temp[3L:])
 | |
|                     (stream.pop(0L) if (index == u'R') else None)
 | |
|                     return parse_stream((acc + [(index, workspace, filename)]), stream)
 | |
|                 _hy_anon_var_5 = _hy_anon_fn_48()
 | |
|             return _hy_anon_var_5
 | |
|         return parse_stream([], stream)
 | |
|     return _hy_anon_fn_50()
 | |
| 
 | |
| def staging_wrapper(files, run_linters):
 | |
| 
 | |
|     def _hy_anon_fn_55():
 | |
| 
 | |
|         def time_gather(f):
 | |
| 
 | |
|             def _hy_anon_fn_52():
 | |
|                 stats = os.stat(f)
 | |
|                 return (f, (stats.atime, stats.mtime))
 | |
|             return _hy_anon_fn_52()
 | |
|         times = list(map(time_gather, files))
 | |
|         run_git_command([u'stash', u'--keep-index'])
 | |
| 
 | |
|         def _hy_anon_fn_54():
 | |
|             results = run_linters(files)
 | |
|             run_git_command([u'reset', u'--hard'])
 | |
|             run_git_command([u'stash', u'pop', u'--quiet', u'--index'])
 | |
|             for (filename, timepair) in times:
 | |
|                 os.utime(filename, timepair)
 | |
|             return results
 | |
|         return _hy_anon_fn_54()
 | |
|     return _hy_anon_fn_55()
 | |
| 
 | |
| def workspace_wrapper(files, run_linters):
 | |
|     return run_linters(files)
 | |
| 
 | |
| def remove_submodules(files):
 | |
| 
 | |
|     def _hy_anon_fn_62():
 | |
| 
 | |
|         def split_out_paths(s):
 | |
|             return s.split(u' ')[2L]
 | |
|         fixer_re = re.compile(u'^(\\.\\.\\/)+')
 | |
| 
 | |
|         def fixer_to_base(s):
 | |
|             return fixer_re.sub(u'', s)
 | |
|         submodule_entries = split_git_response([u'submodule', u'status'])
 | |
| 
 | |
|         def _hy_anon_fn_60(s):
 | |
|             return fixer_to_base(split_out_paths(s))
 | |
|         submodule_names = map(_hy_anon_fn_60, submodule_entries)
 | |
| 
 | |
|         def _hy_anon_fn_61(s):
 | |
|             return (not (s in submodule_names))
 | |
|         return filter(_hy_anon_fn_61, submodule_names)
 | |
|     return _hy_anon_fn_62()
 | |
| 
 | |
| def base_file_filter(files):
 | |
| 
 | |
|     def _hy_anon_fn_64(f):
 | |
|         return os.path.join(git_base, f)
 | |
|     return map(_hy_anon_fn_64, files)
 | |
| 
 | |
| def cwd_file_filter(files):
 | |
| 
 | |
|     def _hy_anon_fn_67():
 | |
|         gitcwd = os.path.join(os.path.relpath(os.getcwd(), git_base), u'')
 | |
| 
 | |
|         def _hy_anon_fn_66(f):
 | |
|             return f.startswith(gitcwd)
 | |
|         return base_file_filter(filter(_hy_anon_fn_66, files))
 | |
|     return _hy_anon_fn_67()
 | |
| 
 | |
| def base_file_cleaner(files):
 | |
| 
 | |
|     def _hy_anon_fn_69(f):
 | |
|         return f.replace(git_base, 1L)
 | |
|     return map(_hy_anon_fn_69, files)
 | |
| 
 | |
| def staging_list():
 | |
| 
 | |
|     def _hy_anon_fn_71(_hy_anon_var_6):
 | |
|         (index, workspace, filename) = _hy_anon_var_6
 | |
|         (index, workspace, filename)
 | |
|         return filename
 | |
| 
 | |
|     def _hy_anon_fn_72(_hy_anon_var_7):
 | |
|         (index, workspace, filename) = _hy_anon_var_7
 | |
|         (index, workspace, filename)
 | |
|         return (index in [u'A', u'M'])
 | |
|     return map(_hy_anon_fn_71, filter(_hy_anon_fn_72, get_porcelain_status))
 | |
| 
 | |
| def working_list():
 | |
| 
 | |
|     def _hy_anon_fn_74(_hy_anon_var_8):
 | |
|         (index, workspace, filename) = _hy_anon_var_8
 | |
|         (index, workspace, filename)
 | |
|         return filename
 | |
| 
 | |
|     def _hy_anon_fn_75(_hy_anon_var_9):
 | |
|         (index, workspace, filename) = _hy_anon_var_9
 | |
|         (index, workspace, filename)
 | |
|         return (working in [u'A', u'M', u'?'])
 | |
|     return map(_hy_anon_fn_74, filter(_hy_anon_fn_75, get_porcelain_status))
 | |
| 
 | |
| def all_list():
 | |
| 
 | |
|     def _hy_anon_fn_78():
 | |
|         cmd = [u'ls-tree', u'--name-only', u'--full-tree', u'-r', git_head]
 | |
| 
 | |
|         def _hy_anon_fn_77(s):
 | |
|             return (len(s) > 0L)
 | |
|         return filter(_hy_anon_fn_77, get_git_response(cmd).split(u'\x00'))
 | |
|     return _hy_anon_fn_78()
 | |
| 
 | |
| def pick_filelist_strategy(options):
 | |
| 
 | |
|     def _hy_anon_fn_81():
 | |
|         keys = options.keys()
 | |
|         working_directory_trans = (base_file_filter if len((set(keys) & set([u'b', u'e']))) else cwd_file_filter)
 | |
|         file_list_generator = (staging_list if (u's' in keys) else (all_list if (u'a' in keys) else (working_list if True else None)))
 | |
| 
 | |
|         def _hy_anon_fn_80():
 | |
|             return working_directory_trans(remove_submodules(file_list_generator()))
 | |
|         return _hy_anon_fn_80
 | |
|     return _hy_anon_fn_81()
 | |
| 
 | |
| def pick_runner(options):
 | |
| 
 | |
|     def _hy_anon_fn_83():
 | |
|         keys = options.keys()
 | |
|         return (staging_wrapper if (u's' in keys) else workspace_wrapper)
 | |
|     return _hy_anon_fn_83()
 | |
| 
 | |
| def run_gitlint(options, config, extras):
 | |
| 
 | |
|     def _hy_anon_fn_85():
 | |
|         file_lister = pick_filelist_strategy(options)
 | |
|         runner = pick_runner(options)
 | |
|         return print(file_lister())
 | |
|     return _hy_anon_fn_85()
 | |
| 
 | |
| def main(*args):
 | |
| 
 | |
|     def _hy_anon_fn_88():
 | |
|         opts = hyopt(optlist, args, u'git lint', u'Copyright (c) 2008, 2016 Kenneth M. "Elf" Sternberg <elf.sternberg@gmail.com>', u'0.0.4')
 | |
|         if (git_base == None):
 | |
|             _hy_anon_var_11 = sys.exit(_(u'Not currently in a git repository.'))
 | |
|         else:
 | |
|             try:
 | |
| 
 | |
|                 def _hy_anon_fn_87():
 | |
|                     options = opts.get_options()
 | |
|                     config = get_config(options, git_base)
 | |
|                     return (opts.print_help() if options.has_key(u'help') else (opts.print_version() if options.has_key(u'version') else (print_linters(config) if options.has_key(u'linters') else (run_gitlint(options, config, opts.filenames) if True else None))))
 | |
|                 _hy_anon_var_10 = _hy_anon_fn_87()
 | |
|             except getopt.GetoptError as err:
 | |
|                 _hy_anon_var_10 = opts.print_help()
 | |
|             _hy_anon_var_11 = _hy_anon_var_10
 | |
|         return _hy_anon_var_11
 | |
|     return _hy_anon_fn_88()
 | |
| if (__name__ == u'__main__'):
 | |
|     import sys
 | |
|     :G_1235 = main(*sys.argv)
 | |
|     _hy_anon_var_12 = (sys.exit(:G_1235) if is_integer(:G_1235) else None)
 | |
| else:
 | |
|     _hy_anon_var_12 = None
 |