From f2873bc07cdbfda37fc6f75cc7c920245da01e2a Mon Sep 17 00:00:00 2001 From: "Elf M. Sternberg" Date: Fri, 23 Sep 2016 17:50:04 -0700 Subject: [PATCH] Conversion to Python is complete. Goodbye, Hy, you were a brilliant and functional step toward helping me organize my thoughts, but it's time to make this into something other Pythonistas can read. Sad, but true. --- .git-lint | 4 +- git_lint/git_lint.py | 473 +++++++++++++++++++------------ git_lint/git_lint_config.py | 33 ++- git_lint/git_lint_options.py | 9 +- git_lint_src/__init__.py | 1 - git_lint_src/git-lint.hy | 287 ------------------- git_lint_src/git-lint.hy.orig | 255 ----------------- git_lint_src/git_lint.hy | 330 --------------------- git_lint_src/git_lint_config.hy | 33 --- git_lint_src/git_lint_options.hy | 77 ----- git_lint_src/load-config.hy | 189 ------------ git_lint_src/n1 | 385 ------------------------- 12 files changed, 330 insertions(+), 1746 deletions(-) delete mode 100644 git_lint_src/__init__.py delete mode 100644 git_lint_src/git-lint.hy delete mode 100644 git_lint_src/git-lint.hy.orig delete mode 100644 git_lint_src/git_lint.hy delete mode 100644 git_lint_src/git_lint_config.hy delete mode 100644 git_lint_src/git_lint_options.hy delete mode 100644 git_lint_src/load-config.hy delete mode 100644 git_lint_src/n1 diff --git a/.git-lint b/.git-lint index 43ae25f..3d310b5 100644 --- a/.git-lint +++ b/.git-lint @@ -8,7 +8,7 @@ condition = output [jshint] output = Running Jshint... -command = jshint -c %(repdir)s/.git-lint/jshint.rc +command = jshint -c %(repodir)s/.git-lint/jshint.rc match = .js print = False condition = error @@ -22,7 +22,7 @@ condition = error [jscs] output = Running JSCS... -command = jscs -c %(repdir)s/.git-lint/jscs.rc +command = jscs -c %(repodir)s/.git-lint/jscs.rc match = .js print = False condition = error diff --git a/git_lint/git_lint.py b/git_lint/git_lint.py index 491d0bd..0d99172 100644 --- a/git_lint/git_lint.py +++ b/git_lint/git_lint.py @@ -6,7 +6,7 @@ import os import re import subprocess import sys -from git_lint_options import hyopt +from git_lint_options import RationalOptions from git_lint_config import get_config _ = gettext.gettext @@ -15,21 +15,45 @@ VERSION = '0.0.2' optlist = [ - ('o', 'only', True, _('A comma-separated list of only those linters to run'), ['exclude']), - ('x', 'exclude', True, _('A comma-separated list of linters to skip'), []), - ('l', 'linters', False, _('Show the list of configured linters')), - ('b', 'base', False, _('Check all changed files in the repository, not just those in the current directory.'), []), - ('a', 'all', False, _('Scan all files in the repository, not just those that have changed.')), - ('e', 'every', False, _('Short for -b -a: scan everything')], ['w', 'workspace', False, _('Scan the workspace'), ['staging']), - ('s', 'staging', False, _('Scan the staging area (useful for pre-commit).'), []), - ('g', 'changes', False, _(u"Report lint failures only for diff'd sections"), ['complete']), - ('p', 'complete', False, _('Report lint failures for all files'), []], ['c', 'config', True, _('Path to config file'), []), - ('h', 'help', False, _('This help message'), []], ['v', 'version', False, _('Version information'), [])] + ('o', 'only', True, + _('A comma-separated list of only those linters to run'), ['exclude']), + ('x', 'exclude', True, + _('A comma-separated list of linters to skip'), []), + ('l', 'linters', False, + _('Show the list of configured linters'), []), + ('b', 'base', False, + _('Check all changed files in the repository, not just those in the current directory.'), []), + ('a', 'all', False, + _('Scan all files in the repository, not just those that have changed.'), []), + ('e', 'every', False, + _('Short for -b -a: scan everything'), []), + ('w', 'workspace', False, + _('Scan the workspace'), ['staging']), + ('s', 'staging', False, + _('Scan the staging area (useful for pre-commit).'), []), + ('g', 'changes', False, + _("Report lint failures only for diff'd sections"), ['complete']), + ('p', 'complete', False, + _('Report lint failures for all files'), []), + ('c', 'config', True, + _('Path to config file'), []), + ('h', 'help', False, + _('This help message'), []), + ('v', 'version', False, + _('Version information'), [])] +# ___ _ _ +# / __(_) |_ +# | (_ | | _| +# \___|_|\__| +# + def get_git_response_raw(cmd): - fullcmd = ([u'git'] + cmd) - process = subprocess.Popen(fullcmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + fullcmd = (['git'] + cmd) + process = subprocess.Popen(fullcmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) (out, err) = process.communicate() return (out, err, process.returncode) @@ -46,23 +70,30 @@ def split_git_response(cmd): def run_git_command(cmd): fullcmd = (['git'] + cmd) - return subprocess.call(fullcmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return subprocess.call(fullcmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) def get_shell_response(fullcmd): - process = subprocess.Popen(fullcmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + process = subprocess.Popen(fullcmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True) (out, err) = process.communicate() return (out, err, process.returncode) def get_git_base(): - (out, error, returncode) = get_git_response_raw(['rev-parse', '--show-toplevel']) + (out, error, returncode) = get_git_response_raw( + ['rev-parse', '--show-toplevel']) return returncode == 0 and out.rstrip() or None def get_git_head(): empty_repository_hash = '4b825dc642cb6eb9a060e54bf8d69288fbee4904' - (out, err, returncode) = get_git_response_raw(['rev-parse', '--verify HEAD']) + (out, err, returncode) = get_git_response_raw( + ['rev-parse', '--verify HEAD']) return (err and empty_repository_hash or 'HEAD') @@ -70,49 +101,58 @@ git_base = get_git_base() git_head = get_git_head() -def encode_shell_messages(prefix, messages): - return ['{}{}'.format(prefix, line.decode('utf-8')) for line in messages.splitlines()] +# _ _ _ _ _ _ _ _ +# | | | | |_(_) (_) |_(_)___ ___ +# | |_| | _| | | | _| / -_|_-< +# \___/ \__|_|_|_|\__|_\___/__/ +# - -def run_external_checker(path, config): - cmd = config[u'command'].format((command + ' "{}"'), path) - (out, err, returncode) = get_shell_response(cmd) - if ((out and (check.get('error_condition', 'error') == 'output')) or err or (not (returncode == 0))): - prefix = check['print_filename'] and '\t{}:'.format(filename) or '\t' - output = encode_shell_messages(prefix, out) + (err and encode_shell_messages(prefix, err) or []) - return [(returncode or 1), output] - return [0, []] +def base_file_cleaner(files): + return [file.replace(git_base, '', 1) for file in files] def make_match_filter_matcher(extensions): - trimmed = reduce(operator.add, [s.strip for s in [ex.split(',') for ex in extension-s]]) + trimmed = reduce(operator.add, [s.strip for s in + [ex.split(',') for ex in extension-s]]) cleaned = [re.sub(r'^\.', s.strip(), '') for s in trimmed] return re.compile(r'\.' + '|'.join(cleaned) + r'$') def make_match_filter(config): - matcher = make_match_filter_matcher([v.get('match', '') for v in config.itervalues()]) + matcher = make_match_filter_matcher([v.get('match', '') + for v in config.itervalues()]) + def match_filter(path): return matcher.search(path) + return match_filter +# ___ _ _ _ _ _ +# / __| |_ ___ __| |__ | (_)_ _| |_ ___ _ _ ___ +# | (__| ' \/ -_) _| / / | | | ' \ _/ -_) '_(_-< +# \___|_||_\___\__|_\_\ |_|_|_||_\__\___|_| /__/ +# + def executable_exists(script, label): if not len(script): - sys.exit(_('Syntax error in command configuration for {} ').format(label)) + sys.exit( + _('Syntax error in command configuration for {} ').format(label)) scriptname = script.split(' ').pop(0) if not len(scriptname): - sys.exit(_('Syntax error in command configuration for {} ').format(label)) + sys.exit( + _('Syntax error in command configuration for {} ').format(label)) - def isexecutable(path): + def is_executable(path): return os.path.exists(path) and os.access(path, os.X_OK) if scriptname.startswith('/'): - return isexecutable(scriptname) and scriptname or None + return is_executable(scriptname) and scriptname or None possibles = [path for path in - [os.path.join(path, scriptname) for path in os.environ.get('PATH').split(':')] + [os.path.join(path, scriptname) + for path in os.environ.get('PATH').split(':')] if is_executable(path)] return len(possibles) and possibles.pop(0) or None @@ -132,204 +172,271 @@ def print_linters(config): print('{:<14} {}'.format(key, _('(WARNING: executable not found)'))) -def base_file_filter(files): - return [os.path.join(git_base, file) for file in files] -def cwd_file_filter(files): - gitcwd = os.path.join(os.path.relpath(os.getcwd(), git_base), '') - return base_file_filter([file for file in files - if file.startswith(gitcwd)]) - - -def base_file_cleaner(files): - return [file.replace(git_base, '', 1) for file in files] - - -MERGE_CONFLICT_PAIRS = set(['DD', 'DU', 'AU', 'AA', 'UD', 'UA', 'UU']) -def check_for_conflicts(filesets): - status_pairs = set(['' + f[0] + f[1] for f in files]) - if len(status_pairs & MERGE_CONFLICT_PAIRS): - sys.exit(_('Current repository contains merge conflicts. Linters will not be run.')) - return filesets - - -def remove_submodules(files): - fixer_re = re.compile('^(\\.\\.\\/)+') - submodules = split_git_response(['submodule', 'status']) - submodule_names = [fixer_re.sub('', submodule.split(' ')[2]) for submodule in submodules] - return [file for file in files if (file not in submodule_names)] - - -def get_porcelain_status(): - cmd = [u'status', u'-z', u'--porcelain', u'--untracked-files=all', u'--ignore-submodules=all'] - stream = [entry for entry in get_git_response(cmd).split(u'\x00') - if len(entry) > 0] - acc = [] - - while len(stream) > 0: - entry = stream.pop(0) - (index, workspace, filename) = (entry[0], entry[1], entry[3:]) - if index == 'R': - stream.pop(0) - acc = acc + [(index, workspace, filename)] - return acc - - -def staging_list(): - return [filename for (index, workspace, filename) in get_porcelain_status() - if index in ['A', 'M']] - - -def working_list(): - return [filename for (index, workspace, filename) in get_porcelain_status() - if workspace in ['A', 'M', '?']] - - -def all_list(): - cmd = ['ls-tree', '--name-only', '--full-tree', '-r', '-z', git_head] - return [file for file in get_git_response(cmd).split(u'\x00') - if len(file) > 0] +# ___ _ _ _ _ __ __ _ _ +# / __|___| |_ | (_)__| |_ ___ / _| / _(_) |___ ___ +# | (_ / -_) _| | | (_-< _| / _ \ _| | _| | / -_|_-< +# \___\___|\__| |_|_/__/\__| \___/_| |_| |_|_\___/__/ +# def get_filelist(options): - keys = options.keys() + """ Returns the list of files against which we'll run the linters. """ + def base_file_filter(files): + """ Return the full path for all files """ + return [os.path.join(git_base, file) for file in files] + + + def cwd_file_filter(files): + """ Return the full path for only those files in the cwd and down """ + gitcwd = os.path.join(os.path.relpath(os.getcwd(), git_base), '') + return base_file_filter([file for file in files + if file.startswith(gitcwd)]) + + + def check_for_conflicts(filesets): + """ Scan list of porcelain files for merge conflic state. """ + MERGE_CONFLICT_PAIRS = set(['DD', 'DU', 'AU', 'AA', 'UD', 'UA', 'UU']) + status_pairs = set(['' + f[0] + f[1] for f in files]) + if len(status_pairs & MERGE_CONFLICT_PAIRS): + sys.exit( + _('Current repository contains merge conflicts. Linters will not be run.')) + return filesets + + + def remove_submodules(files): + """ Remove all submodules from the list of files git-lint cares about. """ + + fixer_re = re.compile('^(\\.\\.\\/)+') + submodules = split_git_response(['submodule', 'status']) + submodule_names = [fixer_re.sub('', submodule.split(' ')[2]) + for submodule in submodules] + return [file for file in files if (file not in submodule_names)] + + + def get_porcelain_status(): + """ Return the status of all files in the system. """ + cmd = ['status', '-z', '--porcelain', + '--untracked-files=all', '--ignore-submodules=all'] + stream = [entry for entry in get_git_response(cmd).split(u'\x00') + if len(entry) > 0] + + def parse_stream(acc, stream): + """Parse the list of files. T + + The list is null-terminated, but is not columnar. If + there's an 'R' in the index state, it means the file was + renamed and the old name is added as a column, so it's a + special case as we accumulate the list of files. + """ + + if len(stream) == 0: + return acc + entry = stream.pop(0) + (index, workspace, filename) = (entry[0], entry[1], entry[3:]) + if index == 'R': + stream.pop(0) + return parse_stream(acc + [(index, workspace, filename)], stream) + + return check_for_conflicts(parse_stream([], stream)) + + + def staging_list(): + """ Return the list of files added or modified to the stage """ + + return [filename for (index, workspace, filename) in get_porcelain_status() + if index in ['A', 'M']] + + + def working_list(): + """ Return the list of files that have been modified in the workspace. + + Includes the '?' to include files that git is not currently tracking. + """ + return [filename for (index, workspace, filename) in get_porcelain_status() + if workspace in ['A', 'M', '?']] + + + def all_list(): + """ Return all the files git is currently tracking for this repository. """ + cmd = ['ls-tree', '--name-only', '--full-tree', '-r', '-z', git_head] + return [file for file in get_git_response(cmd).split(u'\x00') + if len(file) > 0] + + + keys = options.keys() working_directory_trans = cwd_file_filter - if len(set(keys) & set([u'base', u'every'])): + if len(set(keys) & set(['base', 'every'])): working_directory_trans = base_file_filter file_list_generator = working_list + if 'all' in keys: + file_list_generator = all_list if 'staging' in keys: file_list_generator = staging_list return working_directory_trans(remove_submodules(file_list_generator)) -def staging_wrapper(run_linters): +# ___ _ _ +# / __| |_ __ _ __ _(_)_ _ __ _ __ __ ___ _ __ _ _ __ _ __ ___ _ _ +# \__ \ _/ _` / _` | | ' \/ _` | \ V V / '_/ _` | '_ \ '_ \/ -_) '_| +# |___/\__\__,_\__, |_|_||_\__, | \_/\_/|_| \__,_| .__/ .__/\___|_| +# |___/ |___/ |_| |_| + +def pick_runner(options): + """Choose a runner. + + This is the operation that will run the linters. It exists to + provide a way to stash the repository, then restore it when + complete. If possible, it attempts to restore the access and + modification times of the file in order to comfort IDEs that are + constantly monitoring file times. + """ + + def staging_wrapper(run_linters): def time_gather(f): stats = os.stat(f) return (f, (stats.atime, stats.mtime)) times = [time_gather(file) for file in files] - run_git_command([u'stash', u'--keep-index']) + run_git_command(['stash', '--keep-index']) results = run_linters() - run_git_command([u'reset', u'--hard']) - run_git_command([u'stash', u'pop', u'--quiet', u'--index']) + run_git_command(['reset', '--hard']) + run_git_command(['stash', 'pop', '--quiet', '--index']) for (filename, timepair) in times: os.utime(filename, timepair) return results + + def workspace_wrapper(run_linters): + return run_linters() -def workspace_wrapper(run_linters): - return run_linters() - -def pick_runner(options): - if 'staging' in options.keys(): - return staging_wrapper - return workspace_wrapper - + return staging in options.keys() and staging_wrapper or workspace_wrapper +# ___ _ _ _ +# | _ \_ _ _ _ | (_)_ _| |_ _ __ __ _ ______ +# | / || | ' \ | | | ' \ _| | '_ \/ _` (_-<_-< +# |_|_\\_,_|_||_| |_|_|_||_\__| | .__/\__,_/__/__/ +# |_| def run_external_linter(filename, linter): - def _hy_anon_fn_83(): - cmd = (((linter[u'command'] + u'"') + filename) + u'"') - (out, err, returncode) = get_shell_response(cmd) - (out, err, returncode) - if ((out and (linter.get(u'condition', u'error') == u'output')) or err or (not (returncode == 0L))): + """Run one linter against one file. + + If the result matches the error condition specified in the + configuration file, return the error code and messages, either + return nothing. + """ + + def encode_shell_messages(prefix, messages): + return ['{}{}'.format(prefix, line.decode('utf-8')) + for line in messages.splitlines()] + + + cmd = linter['command'] + '"' + filename + '"' + (out, err, returncode) = get_shell_response(cmd) + if ((out and (linter.get('condition', 'error') == 'output')) or err or (not (returncode == 0L))): + prefix = linter.get('print', False) and '\t{}:'.format(filename) or '\t' + output = encode_shell_messages(prefix, out) + (err and encode_shell_messages(prefix, err) or []) + return ((returncode or 1), output) + return (0, []) - def _hy_anon_fn_82(): - prefix = (u'\t{}:'.format(filename) if linter[u'print'] else u'\t') - output = (encode_shell_messages(prefix, out) + (encode_shell_messages(prefix, err) if err else [])) - return [(returncode or 1L), output] - _hy_anon_var_11 = _hy_anon_fn_82() - else: - _hy_anon_var_11 = [0L, []] - return _hy_anon_var_11 - return _hy_anon_fn_83() def run_one_linter(linter, filenames): - def _hy_anon_fn_86(): - match_filter = make_match_filter(linter) - config = linter.values()[0L] - files = set(filter(match_filter, filenames)) + """ Runs one linter against a set of files + + Creates a match filter for the linter, extract the files to be + linted, and runs the linter against each file, returning the + result as a list of successes and failures. Failures have a + return code and the output of the lint process. + """ + + match_filter = make_match_filter(linter) + config = linter.values()[0] + files = set([file for file in filenames if match_filter(file)]) + return [run_external_linter(file, config) for file in files] - def _hy_anon_fn_85(f): - return run_external_linter(f, config) - return list(map(_hy_anon_fn_85, files)) - return _hy_anon_fn_86() def build_lint_runner(linters, filenames): - def _hy_anon_fn_90(): + """ Returns a function to run a set of linters against a set of filenames - def _hy_anon_fn_89(): - keys = sorted(linters.keys()) + This returns a function because it's going to be wrapped in a + runner to better handle stashing and restoring a staged commit. + """ + def lint_runner(): + keys = sorted(linters.keys()) + return [run_one_linter({key: linters[key]}, filenames) for key in keys] + return lint_runner - def _hy_anon_fn_88(key): - return run_one_linter({key: linters[key], }, filenames) - return map(_hy_anon_fn_88, keys) - return _hy_anon_fn_89() - return _hy_anon_fn_90 -def subset_config(config, keys): +# __ __ _ +# | \/ |__ _(_)_ _ +# | |\/| / _` | | ' \ +# |_| |_\__,_|_|_||_| +# - def _hy_anon_fn_92(): - ret = {} - for item in config.items(): - if (item[0L] in keys): - ret[item[0L]] = item[1L] - _hy_anon_var_12 = None - else: - _hy_anon_var_12 = None - return ret - return _hy_anon_fn_92() def run_gitlint(options, config, extras): - def _hy_anon_fn_94(): - all_files = get_filelist(options) - runner = pick_runner(options) - match_filter = make_match_filter(config) - lintable_files = set(filter(match_filter, all_files)) - unlintables = (set(all_files) - lintable_files) - working_linters = get_working_linters(config) - broken_linters = (set(config) - set(working_linters)) - cant_lint_filter = make_match_filter(subset_config(config, broken_linters)) - cant_lintable = set(filter(cant_lint_filter, lintable_files)) - lint_runner = build_lint_runner(subset_config(config, working_linters), lintable_files) - results = runner(lint_runner) - print(u'No Linter Available:', list(unlintables)) - print(u'Linter Executable Not Found for:', list(cant_lintable)) - return print(list(results)) - return _hy_anon_fn_94() + def build_config_subset(keys): + """ Returns a subset of the configuration, with only those linters mentioned in keys """ + return {item[0]: item[1] for item in config.items() if item[0] in keys} + + """ Runs the requested linters """ + all_files = get_filelist(options) + runner = pick_runner(options) + lintable = make_match_filter(config) + lintable_files = set([file for file in all_files if lintable(file)]) + unlintables = (set(all_files) - lintable_files) + working_linters = get_working_linters(config) + broken_linters = (set(config) - set(working_linters)) + cant_lint = make_match_filter(subset_config(config, broken_linters)) + cant_lintable = set([file for file in lintable_files if cant_lint(file)]) + lint_runner = build_lint_runner(build_config_subset(working_linters), lintable_files) + results = runner(lint_runner) + print(list(results)) + return results + def main(*args): + if git_base is None: + sys.exit(_('Not currently in a git repository.')) - def _hy_anon_fn_97(): - opts = hyopt(optlist, args, u'git lint', u'Copyright (c) 2008, 2016 Kenneth M. "Elf" Sternberg ', u'0.0.4') - if (git_base == None): - _hy_anon_var_14 = sys.exit(_(u'Not currently in a git repository.')) - else: - try: + opts = RationalOptions( + optlist, args, + 'git lint', + 'Copyright (c) 2008, 2016 Kenneth M. "Elf" Sternberg ', + '0.0.4') - def _hy_anon_fn_96(): - 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_13 = _hy_anon_fn_96() - except getopt.GetoptError as err: - _hy_anon_var_13 = opts.print_help() - _hy_anon_var_14 = _hy_anon_var_13 - return _hy_anon_var_14 - return _hy_anon_fn_97() -if (__name__ == u'__main__'): + try: + options = opts.get_options() + config = get_config(options, git_base) + if 'help' in options: + opts.print_help() + return 0 + + if 'version' in options: + opts.print_version() + return 0 + + if 'linters' in options: + opts.print_linters() + return 0 + + return run_gitlint(options, config, opts.filenames) + + except getopt.GetoptError as err: + opts.print_help() + return 0 + + +if __name__ == '__main__': import sys - :G_1235 = main(*sys.argv) - _hy_anon_var_15 = (sys.exit(:G_1235) if is_integer(:G_1235) else None) -else: - _hy_anon_var_15 = None + sys.exit(main(*sys.argv)) diff --git a/git_lint/git_lint_config.py b/git_lint/git_lint_config.py index ba1da72..15bfa30 100644 --- a/git_lint/git_lint_config.py +++ b/git_lint/git_lint_config.py @@ -6,7 +6,21 @@ import ConfigParser _ = gettext.gettext -def _find_config_file(options, base): +# (commandLineDictionary, repositoryLocation) -> (configurationFilePath | exit) + +def find_config_file(options, base): + """ Returns the configuration file from a prioritized list of locations. + + Locations are prioritized as: + 1. From the command line. Fail if specified but not found + 2. The repository's root directory, as the file .git-lint + 3. The repository's root directory, as the file .git-lint/config + 4. The user's home directory, as file .git-lint + 5. The user's home directory, as the file .git-lint/config + + If no configuration file is found, this is an error. + """ + if 'config' in options: config = options['config'] configpath = os.path.abspath(config) @@ -27,10 +41,23 @@ def _find_config_file(options, base): return matches[0] +# (commandLineDictionary, repositoryLocation) -> (configurationDictionary | exit) + def get_config(options, base): + """Loads the git-lint configuration file. + + Returns the configuration file as a dictionary of dictionaries. + Performs substitutions as specified in the SafeConfigParser + specification; the only one performed currently is the 'repodir' + will be replaced with the base directory of the repository. + Combined with the option to specify the .git-lint configuration as + a directory, this allows users to keep per-project configuration + files for specific linters. + """ + path = find_config_file(options, base) configloader = ConfigParser.SafeConfigParser() configloader.read(path) - configloader.set('DEFAULT', 'repdir', base) - return {section: {k, v for (k, v) in configloader.items(section)} + configloader.set('DEFAULT', 'repodir', base) + return {section: {k: v for (k, v) in configloader.items(section)} for section in configloader.sections()} diff --git a/git_lint/git_lint_options.py b/git_lint/git_lint_options.py index 04db7c3..ba94fab 100644 --- a/git_lint/git_lint_options.py +++ b/git_lint/git_lint_options.py @@ -8,7 +8,10 @@ import gettext _ = gettext.gettext +# -> scriptNameString + def get_script_name(): + """ Returns the best possible name for the script. """ if getattr(sys, 'frozen', False): (path, name) = os.path.split(sys.executable) return name @@ -19,6 +22,8 @@ def get_script_name(): return names.pop() +# OptionTupleList -> (getOptOptions -> dictionaryOfOptions) + def make_options_rationalizer(optlist): """Takes a list of option tuples, and returns a function that takes the output of getopt and reduces it to the longopt key and @@ -46,6 +51,8 @@ def make_options_rationalizer(optlist): return rationalizer +# (OptionTupleList, dictionaryOfOptions) -> (dictionaryOfOptions, excludedOptions) + def remove_conflicted_options(optlist, request): """Takes our list of option tuples, and a cleaned copy of what was requested from getopt, and returns a copy of the request @@ -102,7 +109,7 @@ class RationalOptions: def print_help(self): print(_('Usage: {} [options] [filenames]').format(self.name)) for item in self.optlist: - print(' -{:<1} --{:<12} {}'.format(item[0L], item[1L], item[3L])) + print(' -{:<1} --{:<12} {}'.format(item[0], item[1], item[3])) return sys.exit() def print_version(self): diff --git a/git_lint_src/__init__.py b/git_lint_src/__init__.py deleted file mode 100644 index 45c40d3..0000000 --- a/git_lint_src/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# foo! diff --git a/git_lint_src/git-lint.hy b/git_lint_src/git-lint.hy deleted file mode 100644 index 4c596d3..0000000 --- a/git_lint_src/git-lint.hy +++ /dev/null @@ -1,287 +0,0 @@ -; -*- mode: clojure -*- -(import os re subprocess sys gettext) -(def *version* "0.0.2") -(def _ gettext.gettext) - -; 0: Short opt, 1: long opt, 2: takes argument, 3: help text -(def optlist [["o" "only" true (_ "A comma-separated list of only those linters to run") ["x"]] - ["x" "exclude" true (_ "A comma-separated list of linters to skip") []] - ["b" "base" false (_ "Check all changed files in the repository, not just those in the current directory.") []] - ["a" "all" false (_ "Scan all files in the repository, not just those that have changed.")] - ["w" "workspace" false (_ "Scan the workspace") ["s"]] - ["s" "staging" false (_ "Scan the staging area (pre-commit).") []] - ["g" "changes" false (_ "Report lint failures only for diff'd sections") ["l"]] - ["l" "complete" false (_ "Report lint failures for all files") []] - ["c" "config" true (_ "Path to config file") []] - ["h" "help" false (_ "This help message") []] - ["v" "version" false (_"Version information") []]]) - -; Given a set of command-line arguments, compare that to a mapped -; version of the optlist and return a canonicalized dictionary of all -; the arguments that have been set. For example "-c" and "--config" -; will both be mapped to "config". - -; Given a prefix of one or two dashes and a position in the above -; array, creates a function to map either the short or long option -; to the option name. - -(defn make-opt-assoc [prefix pos] - (fn [acc it] (assoc acc (+ prefix (get it pos)) (get it 1)) acc)) - -; Using the above, create a full map of all arguments, then return a -; function ready to look up any argument and return the option name. - -(defn make-options-rationalizer [optlist] - (let [ - [short-opt-assoc (make-opt-assoc "-" 0)] - [long-opt-assoc (make-opt-assoc "--" 1)] - [fullset - (ap-reduce (-> (short-opt-assoc acc it) - (long-opt-assoc it)) optlist {})]] - (fn [acc it] (do (assoc acc (get fullset (get it 0)) (get it 1)) acc)))) - - - - -(defn print-version [] - (print (.format "git-lint (hy version {})" *version*)) - (print "Copyright (c) 2008, 2014 Kenneth M. \"Elf\" Sternberg ") - (sys.exit)) - -(defn print-help [] - (print "Usage: git lint [options] [filename]") - (ap-each optlist (print (.format " -{} --{} {}" (get it 0) (get it 1) (get it 3)))) - (sys.exit)) - -; `lint` should be a directory under your .git where you store the RCs -; for your various linters. If you want to use a global one, you'll -; have to edit the configuration entries below. - -(def *config-path* - - - (os.path.join (.get os.environ "GIT_DIR" "./.git") "lint")) - -(def *git-modified-pattern* (.compile re "^[MA]\s+(?P.*)$")) - -(def *checks* - [ -; { -; "output" "Checking for debugger commands in Javascript..." -; "command" "grep -n debugger {filename}" -; "match_files" [".*\.js$"] -; "print_filename" True -; "error_condition" "output" -; } - { - "output" "Running Jshint..." - "command" "jshint -c {config_path}/jshint.rc {filename}" - "match_files" [".*\.js$"] - "print_filename" False - "error_condition" "error" - } - { - "output" "Running Coffeelint..." - "command" "coffeelint {filename}" - "match_files" [".*\.coffee$"] - "print_filename" False - "error_condition" "error" - } - { - "output" "Running JSCS..." - "command" "jscs -c {config_path}/jscs.rc {filename}" - "match_files" [".*\.js$"] - "print_filename" False - "error_condition" "error" - } - { - "output" "Running pep8..." - "command" "pep8 -r --ignore=E501,W293,W391 {filename}" - "match_files" [".*\.py$"] - "print_filename" False - "error_condition" "error" - } - { - "output" "Running xmllint..." - "command" "xmllint {filename}" - "match_files" [".*\.xml"] - "print_filename" False - "error_condition" "error" - } - ] - ) - -(defn get-git-response [cmd] - (let [[fullcmd (+ ["git"] cmd)] - [process (subprocess.Popen fullcmd - :stdout subprocess.PIPE - :stderr subprocess.PIPE)] - [(, out err) (.communicate process)]] - (, out err process.returncode))) - -(defn run-git-command [cmd] - (let [[fullcmd (+ ["git"] cmd)]] - (subprocess.call fullcmd - :stdout subprocess.PIPE - :stderr subprocess.PIPE))) - -(defn get-shell-response [fullcmd] - (let [[process (subprocess.Popen fullcmd - :stdout subprocess.PIPE - :stderr subprocess.PIPE - :shell True)] - [(, out err) (.communicate process)]] - (, out err process.returncode))) - -(defn derive-max-code [code-pairs] - (reduce - (fn [m i] (if (> (abs (get i 0)) (abs m)) (get i 0) m)) - code-pairs 0)) - -(defn derive-message-bodies [code-pairs] - (lmap (fn [i] (get i 1)) code-pairs)) - -(defn lmap (pred iter) (list (map pred iter))) - -(defn encode-shell-messages [prefix messages] - (lmap (fn [line] (.format "{}{}" prefix (.decode line "utf-8"))) - (.splitlines messages))) - -(defn run-external-checker [filename check] - (let [[cmd (-> (get check "command") - (.format - :filename filename - :config_path *config-path*))] - [(, out err returncode) (get-shell-response cmd)]] - (if (or (and out (= (.get check "error_condition" "error") "output")) - err - (not (= returncode 0))) - (let [[prefix (if (get check "print_filename") - (.format "\t{}:" filename) - "\t")] - [output (+ (encode-shell-messages prefix out) - (if err (encode-shell-messages prefix err) []))]] - [(or returncode 1) output]) - [0 []]))) - -(defn matches-file [filename match-files] - (any (map (fn [match-file] (-> (.compile re match-file) - (.match filename))) - match-files))) - -(defn check-scan-wanted [filename check] - (cond [(and (in "match_files" check) - (not (matches-file filename (get check "match_files")))) false] - [(and (in "ignore_files" check) - (matches-file filename (get check "ignore_files"))) false] - [true true])) - -(defn check-files [filenames check] - (let [[filenames-to-check - (filter (fn [filename] (check-scan-wanted filename check)) filenames)] - [results-of-checks - (lmap (fn [filename] - (run-external-checker filename check)) filenames-to-check)] - [messages (+ [(get check "output")] - (derive-message-bodies results-of-checks))]] - [(derive-max-code results-of-checks) messages])) - -(defn gather-all-filenames [] - (let [[build-filenames - (fn [filenames] - (map - (fn [f] (os.path.join (get filenames 0) f)) (get filenames 2)))]] - (list - (flatten - (list-comp (build-filenames o) [o (.walk os ".")]))))) - -(defn gather-staged-filenames [against] - (let [[(, out err returncode) - (get-git-response ["diff-index" "--name-status" against])] - [lines (.splitlines out)] - [matcher - (fn [line] - (.match *git-modified-pattern* (.decode line "utf-8")))]] - (list - (filter - (fn [x] (not (= x ""))) - (list-comp (.group match "name") [match (map matcher lines)] match))))) - -(defn run-checks-for [scan-all-files against] - (do - (run-git-command ["stash" "--keep-index"]) - (let [[filenames-to-scan - (if scan-all-files - (gather-all-filenames) - (gather-staged-filenames against))] - [results-of-scan - (lmap (fn [check] (check-files filenames-to-scan check)) *checks*)] - [exit-code (derive-max-code results-of-scan)] - [messages (flatten (derive-message-bodies results-of-scan))]] - (do - (for [line messages] (print line)) - (run-git-command ["reset" "--hard"]) - (run-git-command ["stash" "pop" "--quiet" "--index"]) - exit-code)))) - -(defn get-head-tag [] - (let [[empty-repository-hash "4b825dc642cb6eb9a060e54bf8d69288fbee4904"] - [(, out err returncode) (get-git-response ["rev-parse" "--verify HEAD"])]] - (if err empty-repository-hash "HEAD"))) - -(defmain [&rest args] - (let [[scan-all-files (and (> (len args) 1) (= (get args 2) "--all-files"))]] - (sys.exit (int (run-checks-for scan-all-files (get-head-tag)))))) - -(defmain [&rest args] - (try - (let [[optstringsshort - (string.join (ap-map (+ (. it [0]) (cond [(. it [2]) ":"] [true ""])) optlist) "")] - [optstringslong - (list (ap-map (+ (. it [1]) (cond [(. it [2]) "="] [true ""])) optlist))] - [(, opt arg) - (getopt.getopt (slice args 1) optstringsshort optstringslong)] - [rationalize-options - (make-options-rationalizer optlist)] - [options - (sanify-options (ap-reduce (rationalize-options acc it) opt {}))]] - - - (cond [(.has_key options "help") (print-help)] - [(.has_key options "version") (print-version)] - [true (suggest options)])) - (catch [err getopt.GetoptError] - (print (str err)) - (print-help)))) - -; staging or workspace -; if workspace: -; modified or all -; CWD or base - -(defn get-porcelain-status [cmd] - (let [[stream (.split (get-git-response ["status" "--porcelain" "--untracked-files=all" "--ignore-submodules=all"]) "\0")] - [parse-stream (fn [acc stream] - (if (= 0 (len stream)) - acc - (let [[temp (.pop stream 0)] - [index (.pop temp 0)] - [workspace (.pop temp 0)] - [filename (slice temp 1)]] - (if (= index "R") - (.pop stream 0)) - (parse-stream (.append acc (, index workspace filename)) stream))))]] - (parse-stream [] stream))) - -(defn modified-in-workspace [s] (s[0] in ["M" "A" "?"])) -(defn modified-in-staging [s] (s[1] in ["M" "A" "?"])) -(defn get-name [s] (s[2])) - -(defn run-staged-scan [options] - (let [[to-scan (filter (fn [a] (in (get (get a 0) 0) ["R" "M"])) - -(defn get-head-tag [] - (let [[empty-repository-hash "4b825dc642cb6eb9a060e54bf8d69288fbee4904"] - [(, out err returncode) (get-git-response ["rev-parse" "--verify HEAD"])]] - (if err empty-repository-hash "HEAD"))) - diff --git a/git_lint_src/git-lint.hy.orig b/git_lint_src/git-lint.hy.orig deleted file mode 100644 index 3304421..0000000 --- a/git_lint_src/git-lint.hy.orig +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/env hy -(import os re subprocess sys gettext) -(def *version* "0.0.2") -(def _ gettext.gettext) - -; 0: Short opt, 1: long opt, 2: takes argument, 3: help text -(def optlist [["o" "only" true (_ "A comma-separated list of only those linters to run") ["x"]] - ["x" "exclude" true (_ "A comma-separated list of linters to skip") []] - ["b" "base" false (_ "Check all changed files in the repository, not just those in the current directory.") []] - ["a" "all" false (_ "Scan all files in the repository, not just those that have changed.")] - ["w" "workspace" false (_ "Scan the workspace") ["s"]] - ["s" "staging" false (_ "Scan the staging area (pre-commit).") []] - ["g" "changes" false (_ "Report lint failures only for diff'd sections") ["l"]] - ["l" "complete" false (_ "Report lint failures for all files") []] - ["c" "config" true (_ "Path to config file") []] - ["h" "help" false (_ "This help message") []] - ["v" "version" false (_"Version information") []]]) - -; Given a set of command-line arguments, compare that to a mapped -; version of the optlist and return a canonicalized dictionary of all -; the arguments that have been set. For example "-c" and "--config" -; will both be mapped to "config". - -; Given a prefix of one or two dashes and a position in the above -; array, creates a function to map either the short or long option -; to the option name. - -(defn make-opt-assoc [prefix pos] - (fn [acc it] (assoc acc (+ prefix (get it pos)) (get it 1)) acc)) - -; Using the above, create a full map of all arguments, then return a -; function ready to look up any argument and return the option name. - -(defn make-options-rationalizer [optlist] - (let [ - [short-opt-assoc (make-opt-assoc "-" 0)] - [long-opt-assoc (make-opt-assoc "--" 1)] - [fullset - (ap-reduce (-> (short-opt-assoc acc it) - (long-opt-assoc it)) optlist {})]] - (fn [acc it] (do (assoc acc (get fullset (get it 0)) (get it 1)) acc)))) - - - - -(defn print-version [] - (print (.format "git-lint (hy version {})" *version*)) - (print "Copyright (c) 2008, 2014 Kenneth M. \"Elf\" Sternberg ") - (sys.exit)) - -(defn print-help [] - (print "Usage: git lint [options] [filename]") - (ap-each optlist (print (.format " -{} --{} {}" (get it 0) (get it 1) (get it 3)))) - (sys.exit)) - -; `lint` should be a directory under your .git where you store the RCs -; for your various linters. If you want to use a global one, you'll -; have to edit the configuration entries below. - -(def *config-path* - - - (os.path.join (.get os.environ "GIT_DIR" "./.git") "lint")) - -(def *git-modified-pattern* (.compile re "^[MA]\s+(?P.*)$")) - -(def *checks* - [ -; { -; "output" "Checking for debugger commands in Javascript..." -; "command" "grep -n debugger {filename}" -; "match_files" [".*\.js$"] -; "print_filename" True -; "error_condition" "output" -; } - { - "output" "Running Jshint..." - "command" "jshint -c {config_path}/jshint.rc {filename}" - "match_files" [".*\.js$"] - "print_filename" False - "error_condition" "error" - } - { - "output" "Running Coffeelint..." - "command" "coffeelint {filename}" - "match_files" [".*\.coffee$"] - "print_filename" False - "error_condition" "error" - } - { - "output" "Running JSCS..." - "command" "jscs -c {config_path}/jscs.rc {filename}" - "match_files" [".*\.js$"] - "print_filename" False - "error_condition" "error" - } - { - "output" "Running pep8..." - "command" "pep8 -r --ignore=E501,W293,W391 {filename}" - "match_files" [".*\.py$"] - "print_filename" False - "error_condition" "error" - } - { - "output" "Running xmllint..." - "command" "xmllint {filename}" - "match_files" [".*\.xml"] - "print_filename" False - "error_condition" "error" - } - ] - ) - -(defn get-git-response [cmd] - (let [[fullcmd (+ ["git"] cmd)] - [process (subprocess.Popen fullcmd - :stdout subprocess.PIPE - :stderr subprocess.PIPE)] - [(, out err) (.communicate process)]] - (, out err process.returncode))) - -(defn run-git-command [cmd] - (let [[fullcmd (+ ["git"] cmd)]] - (subprocess.call fullcmd - :stdout subprocess.PIPE - :stderr subprocess.PIPE))) - -(defn get-shell-response [fullcmd] - (let [[process (subprocess.Popen fullcmd - :stdout subprocess.PIPE - :stderr subprocess.PIPE - :shell True)] - [(, out err) (.communicate process)]] - (, out err process.returncode))) - -(defn derive-max-code [code-pairs] - (reduce - (fn [m i] (if (> (abs (get i 0)) (abs m)) (get i 0) m)) - code-pairs 0)) - -(defn derive-message-bodies [code-pairs] - (lmap (fn [i] (get i 1)) code-pairs)) - -(defn lmap (pred iter) (list (map pred iter))) - -(defn encode-shell-messages [prefix messages] - (lmap (fn [line] (.format "{}{}" prefix (.decode line "utf-8"))) - (.splitlines messages))) - -(defn run-external-checker [filename check] - (let [[cmd (-> (get check "command") - (.format - :filename filename - :config_path *config-path*))] - [(, out err returncode) (get-shell-response cmd)]] - (if (or (and out (= (.get check "error_condition" "error") "output")) - err - (not (= returncode 0))) - (let [[prefix (if (get check "print_filename") - (.format "\t{}:" filename) - "\t")] - [output (+ (encode-shell-messages prefix out) - (if err (encode-shell-messages prefix err) []))]] - [(or returncode 1) output]) - [0 []]))) - -(defn matches-file [filename match-files] - (any (map (fn [match-file] (-> (.compile re match-file) - (.match filename))) - match-files))) - -(defn check-scan-wanted [filename check] - (cond [(and (in "match_files" check) - (not (matches-file filename (get check "match_files")))) false] - [(and (in "ignore_files" check) - (matches-file filename (get check "ignore_files"))) false] - [true true])) - -(defn check-files [filenames check] - (let [[filenames-to-check - (filter (fn [filename] (check-scan-wanted filename check)) filenames)] - [results-of-checks - (lmap (fn [filename] - (run-external-checker filename check)) filenames-to-check)] - [messages (+ [(get check "output")] - (derive-message-bodies results-of-checks))]] - [(derive-max-code results-of-checks) messages])) - -(defn gather-all-filenames [] - (let [[build-filenames - (fn [filenames] - (map - (fn [f] (os.path.join (get filenames 0) f)) (get filenames 2)))]] - (list - (flatten - (list-comp (build-filenames o) [o (.walk os ".")]))))) - -(defn gather-staged-filenames [against] - (let [[(, out err returncode) - (get-git-response ["diff-index" "--name-status" against])] - [lines (.splitlines out)] - [matcher - (fn [line] - (.match *git-modified-pattern* (.decode line "utf-8")))]] - (list - (filter - (fn [x] (not (= x ""))) - (list-comp (.group match "name") [match (map matcher lines)] match))))) - -(defn run-checks-for [scan-all-files against] - (do - (run-git-command ["stash" "--keep-index"]) - (let [[filenames-to-scan - (if scan-all-files - (gather-all-filenames) - (gather-staged-filenames against))] - [results-of-scan - (lmap (fn [check] (check-files filenames-to-scan check)) *checks*)] - [exit-code (derive-max-code results-of-scan)] - [messages (flatten (derive-message-bodies results-of-scan))]] - (do - (for [line messages] (print line)) - (run-git-command ["reset" "--hard"]) - (run-git-command ["stash" "pop" "--quiet" "--index"]) - exit-code)))) - -(defn get-head-tag [] - (let [[empty-repository-hash "4b825dc642cb6eb9a060e54bf8d69288fbee4904"] - [(, out err returncode) (get-git-response ["rev-parse" "--verify HEAD"])]] - (if err empty-repository-hash "HEAD"))) - -(defmain [&rest args] - (let [[scan-all-files (and (> (len args) 1) (= (get args 2) "--all-files"))]] - (sys.exit (int (run-checks-for scan-all-files (get-head-tag)))))) - -(defmain [&rest args] - (try - (let [[optstringsshort - (string.join (ap-map (+ (. it [0]) (cond [(. it [2]) ":"] [true ""])) optlist) "")] - [optstringslong - (list (ap-map (+ (. it [1]) (cond [(. it [2]) "="] [true ""])) optlist))] - [(, opt arg) - (getopt.getopt (slice args 1) optstringsshort optstringslong)] - [rationalize-options - (make-options-rationalizer optlist)] - [options - (sanify-options (ap-reduce (rationalize-options acc it) opt {}))]] - - - (cond [(.has_key options "help") (print-help)] - [(.has_key options "version") (print-version)] - [true (suggest options)])) - (catch [err getopt.GetoptError] - (print (str err)) - (print-help)))) diff --git a/git_lint_src/git_lint.hy b/git_lint_src/git_lint.hy deleted file mode 100644 index 9abf9c3..0000000 --- a/git_lint_src/git_lint.hy +++ /dev/null @@ -1,330 +0,0 @@ -#!/usr/bin/env hy ; -*- mode: clojure -*- -(import ConfigParser os subprocess operator re gettext sys getopt) -(.append sys.path "Users/ksternberg/build/git-lint/git_lint_src") -(import [git-lint-options [hyopt]]) -(import [git-lint-config [get-config]]) - -(def _ gettext.gettext) -(def *version* "0.0.2") - -(defn tap [a] (print "TAP:" a) a) - ; short-name long-name takes-args description precludes -(def optlist [["o" "only" true (_ "A comma-separated list of only those linters to run") ["exclude"]] - ["x" "exclude" true (_ "A comma-separated list of linters to skip") []] - ["l" "linters" false (_ "Show the list of configured linters")] - ["b" "base" false (_ "Check all changed files in the repository, not just those in the current directory.") []] - ["a" "all" false (_ "Scan all files in the repository, not just those that have changed.")] - ["e" "every" false (_ "Short for -b -a: scan everything")] - ["w" "workspace" false (_ "Scan the workspace") ["staging"]] - ["s" "staging" false (_ "Scan the staging area (useful for pre-commit).") []] - ["g" "changes" false (_ "Report lint failures only for diff'd sections") ["complete"]] - ["p" "complete" false (_ "Report lint failures for all files") []] - ["c" "config" true (_ "Path to config file") []] - ["h" "help" false (_ "This help message") []] - ["v" "version" false (_"Version information") []]]) - -(defn get-git-response-raw [cmd] - (let [[fullcmd (+ ["git"] cmd)] - [process (subprocess.Popen fullcmd - :stdout subprocess.PIPE - :stderr subprocess.PIPE)] - [(, out err) (.communicate process)]] - (, out err process.returncode))) - -(defn get-git-response [cmd] - (let [[(, out error returncode) (get-git-response-raw cmd)]] out)) - -(defn split-git-response [cmd] - (let [[(, out error returncode) (get-git-response-raw cmd)]] (.splitlines out))) - -(defn run-git-command [cmd] - (let [[fullcmd (+ ["git"] cmd)]] - (subprocess.call fullcmd - :stdout subprocess.PIPE - :stderr subprocess.PIPE))) - -(defn get-shell-response [fullcmd] - (let [[process (subprocess.Popen fullcmd - :stdout subprocess.PIPE - :stderr subprocess.PIPE - :shell True)] - [(, out err) (.communicate process)]] - (, out err process.returncode))) - -(def git-base (let [[(, out error returncode) - (get-git-response-raw ["rev-parse" "--show-toplevel"])]] - (if (not (= returncode 0)) None (.rstrip out)))) - -; That mystery number is the precise hash code for a repository for has been initialized, -; but for which nothing has ever been added or committed. An empty repository has no refs -; at all so you can't use HEAD in this one very rare case. -(def git-head - (let [[empty-repository-hash "4b825dc642cb6eb9a060e54bf8d69288fbee4904"] - [(, out err returncode) (get-git-response-raw ["rev-parse" "--verify HEAD"])]] - (if (not err) "HEAD" empty-repository-hash))) - - -(defn run-external-checker [path config] - (let [[cmd (-> (get config "command") - (.format (+ command " \"{}\"") path))] - [(, out err returncode) (get-shell-response cmd)]] - (if (or (and out (= (.get check "error_condition" "error") "output")) - err - (not (= returncode 0))) - (let [[prefix (if (get check "print_filename") - (.format "\t{}:" filename) - "\t")] - [output (+ (encode-shell-messages prefix out) - (if err (encode-shell-messages prefix err) []))]] - [(or returncode 1) output]) - [0 []]))) - -; ___ _ _ ___ _ _ ___ _ _ -;| __(_) |___ _ _ __ _ _ __ ___ | __|_ _| |_ ___ _ _ __(_)___ _ _ / __| |_ ___ __| |__ -;| _|| | / -_) ' \/ _` | ' \/ -_) | _|\ \ / _/ -_) ' \(_-< / _ \ ' \ | (__| ' \/ -_) _| / / -;|_| |_|_\___|_||_\__,_|_|_|_\___| |___/_\_\\__\___|_||_/__/_\___/_||_| \___|_||_\___\__|_\_\ -; - -(defn make-match-filter-matcher [extensions] - (->> (map (fn [s] (.split s ",")) extensions) - (reduce operator.add) - (map (fn [s] (.strip s))) - (set) - (filter (fn [s] (not (= 0 (len s))))) - (map (fn [s] (.sub re "^\." "" s))) - (.join "|") - ((fn [s] (+ "\.(" s ")$"))) - ((fn [s] (re.compile s re.I))))) - -(defn make-match-filter [config] - (let [[matcher (make-match-filter-matcher (map (fn [v] (.get v "match" "" )) (.itervalues config)))]] - (fn [path] (.search matcher path)))) - -; _ _ _ _ _ _ _ _ -;| | (_)_ _| |_ ___ _ _ _____ _____ __ _ _| |_ __ _| |__| |___ __| |_ __ _| |_ _ _ ___ -;| |__| | ' \ _/ -_) '_| / -_) \ / -_) _| || | _/ _` | '_ \ / -_) (_-< _/ _` | _| || (_-< -;|____|_|_||_\__\___|_| \___/_\_\___\__|\_,_|\__\__,_|_.__/_\___| /__/\__\__,_|\__|\_,_/__/ -; - -(defn executable-exists [script label] - (if (not (len script)) - (sys.exit (.format (_ "Syntax error in command configuration for {} ") label)) - (let [[scriptname (get (.split script " ") 0)] [paths (.split (.get os.environ "PATH") ":")] - [isexecutable (fn [p] (and (os.path.exists p) (os.access p os.X_OK)))]] - (if (not (len scriptname)) - (sys.exit (.format (_ "Syntax error in command configuration for {} ") label)) - (if (= (get scriptname 0) "/") - (if (isexecutable scriptname) - scriptname None) - (let [[possibles (list (filter (fn [path] (isexecutable (os.path.join path scriptname))) paths))]] - (if (len possibles) - (get possibles 0) None))))))) - -(defn get-working-linters [config] - (let [[found (fn [key] (executable-exists (.get (.get config key) "command") key))]] - (set (filter found (.keys config))))) - -(defn print-linters [config] - (print (_ "Currently supported linters:")) - (let [[working (get-working-linters config)] - [broken (- (set (.keys config)) working)]] - (for [key (sorted working)] - (print (.format "{:<14} {}" key (.get (.get config key) "comment" "")))) - (for [key (sorted broken)] - (print (.format "{:<14} {}" key (_ "(WARNING: executable not found)")))))) - -; ___ _ _ _ _ __ _ _ _ -;| __(_) |___ _ __ __ _| |_| |_ / _(_) | |_ ___ _ _ ___ -;| _|| | / -_) | '_ \/ _` | _| ' \ | _| | | _/ -_) '_(_-< -;|_| |_|_\___| | .__/\__,_|\__|_||_| |_| |_|_|\__\___|_| /__/ -; |_| - -(defn base-file-filter [files] - (map (fn [f] (os.path.join git-base f)) files)) - -(defn cwd-file-filter [files] - (let [[gitcwd (os.path.join (os.path.relpath (os.getcwd) git-base) "")]] - (base-file-filter (filter (fn [f] (.startswith f gitcwd)) files)))) - -(defn base-file-cleaner [files] - (map (fn [f] (.replace f git-base 1)) files)) - -; ___ __ _ _ _ _ _ _ -;| _ \__ ___ __ __ / _(_) |___ | (_)__| |_ __ _ ___ _ _ ___ _ _ __ _| |_ ___ _ _ ___ -;| / _` \ V V / | _| | / -_) | | (_-< _| / _` / -_) ' \/ -_) '_/ _` | _/ _ \ '_(_-< -;|_|_\__,_|\_/\_/ |_| |_|_\___| |_|_/__/\__| \__, \___|_||_\___|_| \__,_|\__\___/_| /__/ -; |___/ - -(def *merge-conflict-pairs* (set ["DD" "DU" "AU" "AA" "UD" "UA" "UU"])) -(defn check-for-conflicts [files] - (let [[status-pairs (map (fn [(, index workspace filename)] (+ "" index workspace)) files)] - [conflicts (& (set *merge-conflict-pairs*) (set status-pairs))]] - (if (len conflicts) - (sys.exit (_ "Current repository contains merge conflicts. Linters will not be run.")) - files))) - -(defn remove-submodules [files] - (let [[split-out-paths (fn [s] (get (.split s " ") 2))] - [fixer-re (re.compile "^(\.\.\/)+")] - [fixer-to-base (fn [s] (.sub fixer-re "" s))] - [submodule-entries (split-git-response ["submodule" "status"])] - [submodule-names (map (fn [s] (fixer-to-base (split-out-paths s))) submodule-entries)]] - (filter (fn [s] (not (in s submodule-names))) files))) - -(defn get-porcelain-status [] - (let [[cmd ["status" "-z" "--porcelain" "--untracked-files=all" "--ignore-submodules=all"]] - [nonnull (fn [s] (> (len s) 0))] - [stream (list (filter nonnull (.split (get-git-response cmd) "\0")))] - [parse-stream (fn [acc stream] - (if (= 0 (len stream)) - acc - (let [[temp (.pop stream 0)] - [index (get temp 0)] - [workspace (get temp 1)] - [filename (slice temp 3)]] - (if (= index "R") - (.pop stream 0)) - (parse-stream (+ acc [(, index workspace filename)]) stream))))]] - (check-for-conflicts (parse-stream [] stream)))) - -(defn staging-list [] - (map (fn [(, index workspace filename)] filename) - (filter (fn [(, index workspace filename)] (in index ["A" "M"])) (get-porcelain-status)))) - -(defn working-list [] - (map (fn [(, index workspace filename)] filename) - (filter (fn [(, index workspace filename)] (in workspace ["A" "M" "?"])) (get-porcelain-status)))) - -(defn all-list [] - (let [[cmd ["ls-tree" "--name-only" "--full-tree" "-r" "-z" git-head]]] - (filter (fn [s] (> (len s) 0)) (.split (get-git-response cmd) "\0")))) - -; _ _ _ __ _ _ _ _ _ _ -; /_\ ______ ___ _ __ | |__| |___ / _(_) |___ | (_)__| |_ __ _ ___ _ _ ___ _ _ __ _| |_ ___ _ _ -; / _ \ (_-<_-" - "0.0.4")]] - (if (= git-base None) - (sys.exit (_ "Not currently in a git repository.")) - (try - (let [[options (.get_options opts)] - [config (get-config options git-base)]] - (cond [(.has_key options "help") (opts.print-help)] - [(.has_key options "version") (opts.print-version)] - [(.has_key options "linters") (print-linters config)] - [true (run-gitlint options config opts.filenames)])) - (catch [err getopt.GetoptError] - (do - (opts.print-help))))))) diff --git a/git_lint_src/git_lint_config.hy b/git_lint_src/git_lint_config.hy deleted file mode 100644 index d40c2c6..0000000 --- a/git_lint_src/git_lint_config.hy +++ /dev/null @@ -1,33 +0,0 @@ -; -*- mode: clojure -*- -(import sys os.path gettext ConfigParser) -(def _ gettext.gettext) - -(defn -find-config-file [options base] - (if (.has_key options "config") - (let [[config (get options "config")] - [configpath (os.path.abspath config)]] - (if (os.path.isfile configpath) - configpath - (sys.exit (.format (_ "Configuration file not found: {}\n") config)))) - (let [[home (os.path.join (.get os.environ "HOME"))] - [possibles (, (os.path.join base ".git-lint") - (os.path.join base ".git-lint/config") - (os.path.join home ".git-lint") - (os.path.join home ".git-lint/config"))] - [matches (list (filter os.path.isfile possibles))]] - (if (len matches) (get matches 0) (sys.exit (_ "No configuration file found")))))) - -(defn -load-config-file [path git-base] - (let [[configloader (.SafeConfigParser ConfigParser)] - [config {}]] - (.read configloader path) - (.set configloader "DEFAULT" "repdir" git-base) - (for [section (.sections configloader)] - (let [[pairs {}]] - (for [(, k v) (.items configloader section)] - (assoc pairs k v)) - (assoc config section pairs))) - config)) - -(defn get-config [options base] - (-load-config-file (-find-config-file options base) base)) diff --git a/git_lint_src/git_lint_options.hy b/git_lint_src/git_lint_options.hy deleted file mode 100644 index 876c316..0000000 --- a/git_lint_src/git_lint_options.hy +++ /dev/null @@ -1,77 +0,0 @@ -; -*- mode: clojure -*- -; Given a set of command-line arguments, compare that to a mapped -; version of the optlist and return a canonicalized dictionary of all -; the arguments that have been set. For example "-c" and "--config" -; will both be mapped to "config". - -; Given a prefix of one or two dashes and a position in the above -; array, creates a function to map either the short or long option -; to the option name. - -(import os sys inspect getopt gettext) -(def _ gettext.gettext) - -(defn get-script-name [] - (if (getattr sys "frozen" False) - (let [[(, path name) (os.path.split sys.executable)]] - (name)) - (let [[prefix (.upper sys.exec_prefix)] - [names (filter (fn [a] (let [[fname (get a 1)]] - (not (or (.startswith fname "<") (.startswith (.upper fname) prefx)))) - (.stack inspect)))] - [name (.pop names)]] - (name)))) - -(defn make-opt-assoc [prefix pos] - (fn [acc it] (assoc acc (+ prefix (get it pos)) (get it 1)) acc)) - -(defn make-options-rationalizer [optlist] - (let [[short-opt-assoc (make-opt-assoc "-" 0)] - [long-opt-assoc (make-opt-assoc "--" 1)] - [fullset (reduce (fn [acc i] (-> (short-opt-assoc acc i) - (long-opt-assoc i))) optlist {})]] - (fn [acc it] (do (assoc acc (get fullset (get it 0)) (get it 1)) acc)))) - -(defn remove-conflicted-options [optlist config] - (let [[keys (.keys config)] - [marked (filter (fn [opt] (in (get opt 1) keys)) optlist)] - [exclude (reduce (fn [memo opt] (+ memo (if (> (len opt) 4) (get opt 4) []))) marked [])] - [excluded (filter (fn [key] (in key exclude)) keys)] - [cleaned (reduce (fn [memo key] - (if (not (in key excluded)) (assoc memo key (get config key))) memo) keys {})]] - (, cleaned excluded))) - -(defclass hyopt [] - [[--init-- (fn [self optlist args &optional [name ""] [copyright ""] [version "0.0.1"]] - (let [[optstringsshort - (.join "" (map (fn [i] (+ (. i [0]) (cond [(. i [2]) ":"] [true ""]))) optlist))] - [optstringslong - (list (map (fn [i] (+ (. i [1]) (cond [(. i [2]) "="] [true ""]))) optlist))] - [(, opt arg) - (getopt.getopt (slice args 1) optstringsshort optstringslong)] - [rationalize-options (make-options-rationalizer optlist)] - [(, newoptions excluded) (remove-conflicted-options - optlist (reduce (fn [acc i] (rationalize-options acc i)) opt {}))]] - (setv self.optlist optlist) - (setv self.options newoptions) - (setv self.excluded excluded) - (setv self.filenames arg) - (setv self.name (if name name (get-script-name))) - (setv self.version version) - (setv self.copyright copyright)) - None)] - - [get-options (fn [self] self.options)] - [get-keys (fn [self] (set (.keys self.options)))] - [print-help (fn [self] - (print (.format (_ "Usage: {} [options] [filenames]") self.name)) - (for [item self.optlist] (print (.format " -{:<1} --{:<12} {}" (get item 0) (get item 1) (get item 3)))) - (sys.exit))] - - [print-version (fn [self] - (print (.format "{}" self.name self.version)) - (if self.copyright - (print self.copyright)) - (sys.exit))]]) - - diff --git a/git_lint_src/load-config.hy b/git_lint_src/load-config.hy deleted file mode 100644 index 78f3d6e..0000000 --- a/git_lint_src/load-config.hy +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env hy -(import ConfigParser os subprocess operator re gettext sys getopt) -(.append sys.path "Users/ksternberg/build/git-lint/git_lint_src") -(import [git-lint-options [hyopt]]) -(import [git-lint-config [get-config]]) - -(def _ gettext.gettext) -(def *version* "0.0.2") - -(defn tap [a] (print "TAP:" a) a) - - ; short-name long-name takes-args description precludes -(def optlist [["o" "only" true (_ "A comma-separated list of only those linters to run") ["exclude"]] - ["x" "exclude" true (_ "A comma-separated list of linters to skip") []] - ["l" "linters" false (_ "Show the list of configured linters")] - ["b" "base" false (_ "Scan from the base directory rather than the current working") []] - ["a" "all" false (_ "Scan all files in the repository, not just those that have changed")] - ["e" "every" false (_ "Short for -b -a: scan everything")] - ["w" "workspace" false (_ "Scan the workspace") ["staging"]] - ["s" "staging" false (_ "Scan the staging area (useful for pre-commit).") ["base" "all" "every" "workspace"]] - ["g" "changes" false (_ "Report lint failures only for diff'd sections") ["complete"]] - ["p" "complete" false (_ "Report lint failures over whole files") []] - ["c" "config" true (_ "Path to config file") []] - ["d" "dryrun" false (_ "Report what git-lint would do, but don't actually do anything.") []] - ["q" "quiet" false (_ "Produce a short report of files that failed to pass.") []] - ["h" "help" false (_ "This help message") []] - ["v" "version" false (_"Version information") []]]) - -(defn get-git-response-raw [cmd] - (let [[fullcmd (+ ["git"] cmd)] - [process (subprocess.Popen fullcmd - :universal-newlines True - :stdout subprocess.PIPE - :stderr subprocess.PIPE)] - [(, out err) (.communicate process)]] - (, out err process.returncode))) - -(defn get-git-response [cmd] - (let [[(, out error returncode) (get-git-response-raw cmd)]] out)) - -(defn split-git-response [cmd] - (let [[(, out error returncode) (get-git-response-raw cmd)]] (.splitlines out))) - -(defn split-git-response [cmd] - (let [[(, out error returncode) (get-git-response-raw cmd)]] (.splitlines out))) - - -(defn run-git-command [cmd] - (let [[fullcmd (+ ["git"] cmd)]] - (subprocess.call fullcmd - :stdout subprocess.PIPE - :stderr subprocess.PIPE))) - -(defn get-shell-response [fullcmd] - (let [[process (subprocess.Popen fullcmd - :stdout subprocess.PIPE - :stderr subprocess.PIPE - :universal-newlines True - :shell True)] - [(, out err) (.communicate process)]] - (, out err process.returncode))) - -(def git-base (let [[(, out error returncode) - (get-git-response-raw ["rev-parse" "--show-toplevel"])]] - (if (not (= returncode 0)) None (.rstrip out)))) - -(defn get-all-from-cwd [] - (split-git-response ["ls-tree" "--name-only" "-r" "HEAD"])) - -(defn get-all-from-base [] - (split-git-response ["ls-tree" "--name-only" "-r" "--full-tree" "HEAD"])) - - ; Any of these indicate the tree is in a merge - ; conflict state and the user has bigger problems. -(def *merge-conflict-pairs* (set ["DD" "DU" "AU" "AA" "UD" "UA" "UU"])) -(defn get-changed-from-source [trackings] - (let [[conflicts (filter (fn [t] (t[0:2] in *merge-conflict-pairs*)) trackings)]] - (if (len conflicts) - (sys.exit (_ "Current repository contains merge conflicts. Linters will not be run.")) - trackings))) - -(defn get-porcelain-status [] - (let [[cmd ["status" "-z" "--porcelain" "--untracked-files=all" "--ignore-submodules=all"]] - [nonnull (fn [s] (> (len s) 0))] - [stream (tap (list (filter nonnull (.split (get-git-response cmd) "\0"))))] - [parse-stream (fn [acc stream] - (if (= 0 (len stream)) - acc - (let [[temp (.pop stream 0)] - [index (get temp 0)] - [workspace (get temp 1)] - [filename (tap (slice temp 3))]] - (if (= index "R") - (.pop stream 0)) - (parse-stream (+ acc [(, index workspace filename)]) stream))))]] - (parse-stream [] stream))) - -(defn modified-in-workspace [s] (in s[0] ["M" "A" "?"])) -(defn modified-in-staging [s] (in s[1] ["M" "A"])) -(defn get-name [s] (s[2])) - - ;(defn get-changed-from-cwd [] - ; (->> (get-changed-from_source (split-git-response ["status" "--porcelain" "--untracked-files=all"])) - ; (filter (fn [s] (s[0] in - ; - ; (map (fn [s] - ; (filter (fn [s] ( - ; - -(defn get-changed-from-base [] - (get-changed-from_source (split-git-response ["status" "--porcelain" "--untracked-files=all" git-base]))) - -(defn get-staged-from-cwd [] - ()) - -(defn gen-staged-from-base [] - ()) - - -(defn make-match-filter-matcher [extensions] - (->> (map (fn [s] (.split s ",")) extensions) - (reduce operator.add) - (map (fn [s] (.strip s))) - (set) - (filter (fn [s] (not (= 0 (len s))))) - (map (fn [s] (.sub re "^\." "" s))) - (.join "|") - ((fn [s] (+ "\.(" s ")$"))) - ((fn [s] (re.compile s re.I))))) - -(defn make-match-filter [config] - (let [[matcher (make-match-filter-matcher (map (fn [v] (.get v "match" "" )) - (.itervalues config)))]] - (fn [path] (.search matcher path)))) - -(defn executable-exists [script label] - (if (not (len script)) - (sys.exit (.format (_ "Syntax error in command configuration for {} ") label)) - (let [[scriptname (get (.split script " ") 0)] - [paths (.split (.get os.environ "PATH") ":")] - [isexecutable (fn [p] (and (os.path.exists p) (os.access p os.X_OK)))]] - (if (not (len scriptname)) - (sys.exit (.format (_ "Syntax error in command configuration for {} ") label)) - (if (= (get scriptname 0) "/") - (if (isexecutable scriptname) - scriptname None) - (let [[possibles (list (filter (fn [path] (isexecutable (os.path.join path scriptname))) paths))]] - (if (len possibles) - (get possibles 0) None))))))) - -(defn print-linters [config] - (print (_ "Currently supported linters:")) - (for [(, linter items) (.iteritems config)] - (if (not (executable-exists (.get items "command" "") linter)) - (print (.format "{:<14} {}" linter (_ "(WARNING: executable not found)"))) - (print (.format "{:<14} {}" linter (.get items "comment" "")))))) - -(defn git-lint-main [options] - (print git-base) - (print (os.path.abspath __file__)) - (let [[config (get-config options git-base)]] - (print options) - (print config) - (print (make-match-filter config)) - (print (get-porcelain-status)))) - - -(defmain [&rest args] - (for [option optlist] - (print (.format "``-{}`` ``--{}``\n {}" (get option 0) (get option 1) (get option 3))))) - - -;(defmain [&rest args] -; (if (= git-base None) -; (sys.exit (_ "Not currently in a git repository.")) -; (try -; (let [[opts (hyopt optlist args "git lint" -; "Copyright (c) 2008, 2016 Kenneth M. \"Elf\" Sternberg " -; "0.0.4")] -; [options opts.options] -; [config (get-config options git-base)]] -; (cond [(.has_key options "help") (opts.print-help)] -; [(.has_key options "version") (opts.print-version)] -; [(.has_key options "linters") (print-linters config)] -; [true (git-lint-main options)])) -; (catch [err getopt.GetoptError] -; (print (str err)) -; (print-help))))) -; diff --git a/git_lint_src/n1 b/git_lint_src/n1 deleted file mode 100644 index ca5898a..0000000 --- a/git_lint_src/n1 +++ /dev/null @@ -1,385 +0,0 @@ -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 ', 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