Compare commits
23 Commits
Author | SHA1 | Date |
---|---|---|
Elf M. Sternberg | e86fe1ff07 | |
Ken Sternberg | 41d61476e1 | |
Elf M. Sternberg | ef2a5c579b | |
Tino de Bruijn | b485697742 | |
Ken Sternberg | 1e26d46d17 | |
Ken Sternberg | e5a8f93630 | |
Ken Sternberg | 09dcb1a050 | |
Ken Sternberg | e11655e3b9 | |
Elf M. Sternberg | 5cc8770ac3 | |
Elf M. Sternberg | 8fec735719 | |
Elf M. Sternberg | a8451c83bf | |
Elf M. Sternberg | 1a942e6b1c | |
Elf M. Sternberg | 9b40abe4ae | |
Elf M. Sternberg | 6e09df1427 | |
Elf M. Sternberg | f70aea7ec3 | |
Elf M. Sternberg | 3461709ebf | |
Elf M. Sternberg | 74d217d6fe | |
Elf M. Sternberg | f12b746ade | |
Elf M. Sternberg | 4895dde1b6 | |
Elf M. Sternberg | d046268ad6 | |
Elf M. Sternberg | 1b5b215e3b | |
Elf M. Sternberg | 556c56fb43 | |
Elf M. Sternberg | a1b5ae9eb0 |
|
@ -10,4 +10,4 @@ Development Lead
|
|||
Contributors
|
||||
------------
|
||||
|
||||
None yet. Why not be the first?
|
||||
* Tino de Bruijn <work@tino.io>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
GitLint is a git command to automatically run a suite of pre-defined linters.
|
|
@ -8,5 +8,6 @@ recursive-include tests *
|
|||
recursive-exclude * __pycache__
|
||||
recursive-exclude * *.py[co]
|
||||
|
||||
recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif
|
||||
recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif *.1
|
||||
include docs/_build/man/git-lint.1
|
||||
include bin/pre-commit bin/git-lint-style
|
||||
|
|
32
README.rst
32
README.rst
|
@ -34,9 +34,18 @@ To lint what's in your staging directory:
|
|||
`git lint -s`
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Complete documentation for the project is available in the docs directory, or at `Git
|
||||
Linter Docs <https://elfsternberg.github.io/git-linter/index.html>`_.
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
This *ought* to work:
|
||||
|
||||
`pip install git-linter`
|
||||
|
||||
You will need to copy the .git-lint configuration file to either your
|
||||
|
@ -44,7 +53,14 @@ home directory or the repo`s base directory. Edit the configuration
|
|||
file as needed. You will also need any linters that you plan on
|
||||
running.
|
||||
|
||||
As git-linter is still mostly alpha code, it might be better to install
|
||||
from source:
|
||||
|
||||
``
|
||||
git clone https://github.com/elfsternberg/git-linter
|
||||
python setup.py install
|
||||
``
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
|
@ -66,12 +82,18 @@ Features
|
|||
|
||||
Acknowledgements
|
||||
----------------
|
||||
`Git lint` started life as a simple pre-commit hook. Most of the
|
||||
changes since were inspired by Steve Pulec's *`Why You Need a Git
|
||||
Pre-Commit Hook and Why Most Are Wrong`_ ,* as well as just my own needs as
|
||||
a software developer.
|
||||
|
||||
.. _Why You Need a Git Pre-Commit Hook and Why Most Are Wrong: https://dzone.com/articles/why-your-need-git-pre-commit
|
||||
`Git lint` started life as a simple pre-commit hook. Most of the changes since were
|
||||
inspired by Steve Pulec's `Why You Need a Git Pre-Commit Hook and Why Most Are Wrong <https://dzone.com/articles/why-your-need-git-pre-commit>`_, as well as just my own needs
|
||||
as a software developer.
|
||||
|
||||
To do
|
||||
-----
|
||||
|
||||
* The '-q' and '--quiet' arguments do not work.
|
||||
* Ought to be able to silence the "no linter found" message.
|
||||
* Ought to be able to configure '-q' and '--silence' commands in .git-lint/config file.
|
||||
* Ought to be able to override config file for above.
|
||||
|
||||
|
||||
Disclaimer
|
||||
|
|
|
@ -111,7 +111,7 @@ pygments_style = 'sphinx'
|
|||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
html_theme = 'alabaster'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a
|
||||
# theme further. For a list of options available for each theme, see the
|
||||
|
|
|
@ -3,8 +3,12 @@
|
|||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to Git Lint's documentation!
|
||||
======================================
|
||||
Git Lint
|
||||
========
|
||||
|
||||
Git Lint provides a new git command that automatically runs identifiable linters (style
|
||||
and syntax quality assurance programs) against changed files in your current git
|
||||
repository or staging area.
|
||||
|
||||
Contents:
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ To install Git Lint, run this command in your terminal:
|
|||
|
||||
.. code-block:: console
|
||||
|
||||
$ pip install git_linter
|
||||
$ pip install git-linter
|
||||
|
||||
If you don't have `pip`_ installed, this `Python installation guide`_ can guide
|
||||
you through the process.
|
||||
|
@ -30,13 +30,13 @@ You can either clone the public repository:
|
|||
|
||||
.. code-block:: console
|
||||
|
||||
$ git clone git://github.com/elfsternberg/git_linter
|
||||
$ git clone git://github.com/elfsternberg/git-linter
|
||||
|
||||
Or download the `tarball`_:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ curl -OL https://github.com/elfsternberg/git_linter/tarball/master
|
||||
$ curl -OL https://github.com/elfsternberg/git-linter/tarball/master
|
||||
|
||||
Once you have a copy of the source, you can install it with:
|
||||
|
||||
|
@ -44,9 +44,8 @@ Once you have a copy of the source, you can install it with:
|
|||
|
||||
$ python setup.py install
|
||||
|
||||
.. _Github repo: https://github.com/elfsternberg/git_linter
|
||||
.. _tarball: https://github.com/elfsternberg/git_linter/tarball/master
|
||||
.. _Github repo: https://github.com/elfsternberg/git-linter
|
||||
.. _tarball: https://github.com/elfsternberg/git-linter/tarball/master
|
||||
|
||||
Once installed, please copy the '.git-lint' example file. You may install this either in
|
||||
your home directory as ``.git-lint`` or in your project's git directory as
|
||||
``.git/lint/git-lint``
|
||||
your home or repository directory as ``.git-lint``.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
=====
|
||||
Usage
|
||||
=====
|
||||
|
||||
|
||||
Command Line
|
||||
------------
|
||||
|
||||
|
@ -12,25 +12,13 @@ git lint [options] [filenames]
|
|||
Options
|
||||
-------
|
||||
|
||||
.. include: arguments.rst
|
||||
.. include:: arguments.rst
|
||||
|
||||
As a pre-commit hook:
|
||||
---------------------
|
||||
|
||||
.. code-block:: python
|
||||
There's a file, pre-commit, in the /bin directory with the project. (Or you can download
|
||||
it from the github repository.) Install it in you .git/hooks/pre-commit file, and
|
||||
chmod +x .git/hooks/pre-commit.
|
||||
|
||||
#!/usr/bin/env python
|
||||
import git_lint
|
||||
git_lint.run_precommit(staging = True, timestamps = True)
|
||||
|
||||
Install this file in your project's ``.git/hooks/pre-commit``, and set
|
||||
the file's executable flag to ``true``:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
chmod +x pre-commit
|
||||
|
||||
Please see the :ref:`api` for more details on options taken by the
|
||||
``run_precommit()`` and ``run_gitlint`` commands.
|
||||
|
||||
There is an example ``pre-commit`` script shipped with ``git lint``.
|
||||
The pre-commit hook is *experimental*. Please be careful with it.
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
__author__ = 'Kenneth M. "Elf" Sternberg'
|
||||
__email__ = 'elf.sternberg@gmail.com'
|
||||
__version__ = '0.0.4'
|
||||
__version__ = '0.0.7'
|
||||
|
||||
__all__ = ['git_lint']
|
||||
|
|
|
@ -11,7 +11,7 @@ import gettext
|
|||
_ = gettext.gettext
|
||||
|
||||
NAME = 'git-lint'
|
||||
VERSION = '0.0.4'
|
||||
VERSION = '0.0.7'
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
@ -9,11 +9,18 @@ import re
|
|||
import subprocess
|
||||
import sys
|
||||
import pprint
|
||||
|
||||
try:
|
||||
import configparser
|
||||
except ImportError as e:
|
||||
import ConfigParser as configparser
|
||||
|
||||
try: # noqa: F401
|
||||
from typing import Dict, List, Text, Any, Optional, Union, Callable, Tuple # noqa: F401
|
||||
except: # noqa: F401
|
||||
pass # noqa: F401
|
||||
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
|
@ -24,39 +31,6 @@ _ = gettext.gettext
|
|||
# |___/
|
||||
|
||||
|
||||
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)
|
||||
if not os.path.isfile(configpath):
|
||||
sys.exit(_('Configuration file not found: {}\n').format(config))
|
||||
return configpath
|
||||
|
||||
home = os.environ.get('HOME', None)
|
||||
possibles = [os.path.join(base, '.git-lint'),
|
||||
os.path.join(base, '.git-lint/config')] + ((home and [
|
||||
os.path.join(home, '.git-lint'),
|
||||
os.path.join(home, '.git-lint/config')]) or [])
|
||||
|
||||
matches = [p for p in possibles if os.path.isfile(p)]
|
||||
if len(matches) == 0:
|
||||
sys.exit(_('No configuration file found, tried: {}').format(':'.join(possibles)))
|
||||
|
||||
return matches[0]
|
||||
|
||||
|
||||
# (commandLineDictionary, repositoryLocation) -> (configurationDictionary | exit)
|
||||
def load_config(options, base):
|
||||
"""Loads the git-lint configuration file.
|
||||
|
@ -70,6 +44,38 @@ def load_config(options, base):
|
|||
files for specific linters.
|
||||
"""
|
||||
|
||||
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)
|
||||
if not os.path.isfile(configpath):
|
||||
sys.exit(_('Configuration file not found: {}\n').format(config))
|
||||
return configpath
|
||||
|
||||
home = os.environ.get('HOME', None)
|
||||
possibles = [os.path.join(base, '.git-lint'),
|
||||
os.path.join(base, '.git-lint/config')] + ((home and [
|
||||
os.path.join(home, '.git-lint'),
|
||||
os.path.join(home, '.git-lint/config')]) or [])
|
||||
|
||||
matches = [p for p in possibles if os.path.isfile(p)]
|
||||
if len(matches) == 0:
|
||||
sys.exit(_('No configuration file found, tried: {}').format(':'.join(possibles)))
|
||||
|
||||
return matches[0]
|
||||
|
||||
Linter = namedtuple('Linter', ['name', 'linter'])
|
||||
path = find_config_file(options, base)
|
||||
configloader = configparser.SafeConfigParser()
|
||||
|
@ -168,37 +174,34 @@ class MatchFilter:
|
|||
# \___|_||_\___\__|_\_\ |_|_|_||_\__\___|_| /__/
|
||||
#
|
||||
|
||||
def executable_exists(script, label):
|
||||
if not len(script):
|
||||
sys.exit(
|
||||
_('Syntax error in command configuration for {} ').format(label))
|
||||
def linter_exists(linter, label):
|
||||
if not len(linter):
|
||||
sys.exit(_('Syntax error in linter configuration for {} ').format(label))
|
||||
|
||||
scriptname = script.split(' ').pop(0)
|
||||
if not len(scriptname):
|
||||
sys.exit(
|
||||
_('Syntax error in command configuration for {} ').format(label))
|
||||
lintername = linter.split(' ').pop(0)
|
||||
if not len(lintername):
|
||||
sys.exit(_('Syntax error in linter configuration for {} ').format(label))
|
||||
|
||||
def is_executable(path):
|
||||
return os.path.exists(path) and os.access(path, os.X_OK)
|
||||
|
||||
if scriptname.startswith('/'):
|
||||
return (is_executable(scriptname) and scriptname) or None
|
||||
if lintername.startswith('/'):
|
||||
return (is_executable(lintername) and lintername) or None
|
||||
|
||||
# shutil.which() doesn't appear until Python 3, darnit.
|
||||
possibles = [path for path in
|
||||
[os.path.join(path, scriptname)
|
||||
[os.path.join(path, lintername)
|
||||
for path in os.environ.get('PATH').split(':')]
|
||||
if is_executable(path)]
|
||||
|
||||
return (len(possibles) and possibles.pop(0)) or False
|
||||
|
||||
|
||||
def get_working_linter_names(config):
|
||||
return [i.name for i in config
|
||||
if executable_exists(i.linter['command'], i.name)]
|
||||
|
||||
|
||||
def get_linter_status(config):
|
||||
def get_working_linter_names(config):
|
||||
return [i.name for i in config
|
||||
if linter_exists(i.linter['command'], i.name)]
|
||||
|
||||
working_linter_names = get_working_linter_names(config)
|
||||
broken_linter_names = (set([i.name for i in config]) - set(working_linter_names))
|
||||
return working_linter_names, broken_linter_names
|
||||
|
@ -272,6 +275,11 @@ def get_filelist(options, extras):
|
|||
|
||||
return check_for_conflicts(parse_stream([], stream))
|
||||
|
||||
def revision_list():
|
||||
cmd = ['diff', '--name-only', '-z', options.get('revision')]
|
||||
return [entry for entry in get_git_response(cmd).split(u'\x00')
|
||||
if len(entry) > 0]
|
||||
|
||||
def staging_list():
|
||||
""" Return the list of files added or modified to the stage """
|
||||
|
||||
|
@ -303,6 +311,9 @@ def get_filelist(options, extras):
|
|||
working_directory_trans = base_file_filter
|
||||
|
||||
file_list_generator = working_list
|
||||
if 'revision' in options:
|
||||
file_list_generator = revision_list
|
||||
working_directory_trans = base_file_filter
|
||||
if 'all' in options:
|
||||
file_list_generator = all_list
|
||||
if 'staging' in options:
|
||||
|
@ -421,6 +432,9 @@ class Linters:
|
|||
|
||||
|
||||
def run_linters(options, config, extras=[]):
|
||||
if 'pr' in options:
|
||||
options.pop('pr')
|
||||
options['revision'] = 'HEAD^..HEAD'
|
||||
|
||||
def build_config_subset(keys):
|
||||
""" Returns a subset of the configuration, with only those linters mentioned in keys """
|
||||
|
@ -430,17 +444,25 @@ def run_linters(options, config, extras=[]):
|
|||
all_filenames, unfindable_filenames = get_filelist(options, extras)
|
||||
|
||||
is_lintable = MatchFilter(config)
|
||||
|
||||
lintable_filenames = set([filename for filename in all_filenames
|
||||
if is_lintable(filename)])
|
||||
|
||||
unlintable_filenames = set(all_filenames) - lintable_filenames
|
||||
|
||||
# Filter the linter config down to the selected ones.
|
||||
if 'only' in options:
|
||||
config = [linter for linter in config
|
||||
if linter.name in options['only']]
|
||||
elif 'exclude' in options:
|
||||
config = [linter for linter in config
|
||||
if linter.name not in options['exclude']]
|
||||
if not len(config):
|
||||
raise RuntimeError('No linters left to run! Be less strict with --only and --exclude.')
|
||||
|
||||
working_linter_names, broken_linter_names = get_linter_status(config)
|
||||
|
||||
cant_lint_filter = MatchFilter(build_config_subset(
|
||||
broken_linter_names))
|
||||
|
||||
cant_lint_filenames = [filename for filename in lintable_filenames
|
||||
if cant_lint_filter(filename)]
|
||||
|
||||
|
|
|
@ -2,8 +2,18 @@
|
|||
# Author: Elf M. Sternberg
|
||||
|
||||
from functools import reduce
|
||||
from collections import namedtuple
|
||||
import getopt
|
||||
|
||||
try: # noqa: F401
|
||||
from typing import Dict, List, Text, Any, Optional, Union, Callable, Tuple # noqa: F401
|
||||
except: # noqa: F401
|
||||
pass # noqa: F401
|
||||
|
||||
|
||||
Option = namedtuple('Option', ['short', 'long', 'takes', 'help', 'conflicts']) # type: str, str, str, str, List[str]
|
||||
Arguments = namedtuple('Arguments', ['arguments', 'filenames', 'excluded']) # type: Dict[str, str], List[str], List[str]
|
||||
|
||||
# This was a lot shorter and smarter in Hy...
|
||||
|
||||
# A lot of what you see here is separated from git_lint itself, since this will not be
|
||||
|
@ -17,6 +27,7 @@ import getopt
|
|||
|
||||
|
||||
def cleanup_options(options, commandline):
|
||||
# type: (List[Option], List[str]) -> Arguments
|
||||
"""Takes a table of options and the commandline, and returns a
|
||||
dictionary of those options that appear on the commandline
|
||||
along with any extra arguments.
|
||||
|
@ -24,41 +35,50 @@ def cleanup_options(options, commandline):
|
|||
:param List(Tuple (string, string, boolean, string, List(string))) options,
|
||||
The table of options: One-letter option, long option, takes arguments,
|
||||
Help text, list of (long) options superseded by this one.
|
||||
|
||||
: param List(strings) commandline
|
||||
The arguments as received by the start-up process
|
||||
|
||||
: returns List(strings), List(strings), List(strings)
|
||||
The longopt dictionary of arguments and associated values (if any)
|
||||
The list of filenames left after argument processing
|
||||
The longopt list of arguments that were excluded by argument precedence
|
||||
"""
|
||||
|
||||
def make_option_streamliner(options):
|
||||
# type: (List[Option]) -> Callable[[Dict[str, str], Option], Dict[str, str]]
|
||||
|
||||
"""Takes a list of option tuples, and returns a function that takes
|
||||
the output of getopt and reduces it to the longopt key and
|
||||
associated values as a dictionary.
|
||||
"""
|
||||
|
||||
fullset = {}
|
||||
fullset = {} # type: Dict[str, str]
|
||||
for option in options:
|
||||
if option[1]:
|
||||
fullset['--' + option[1]] = option[1]
|
||||
if option[0]:
|
||||
fullset['-' + option[0]] = option[1]
|
||||
if option.long:
|
||||
fullset['--' + option.long] = option.long
|
||||
if option.short:
|
||||
fullset['-' + option.short] = option.long
|
||||
|
||||
def streamliner(acc, it):
|
||||
# type: (Dict[str, str], Option) -> Dict[str, str]
|
||||
acc[fullset[it[0]]] = it[1]
|
||||
return acc
|
||||
|
||||
return streamliner
|
||||
|
||||
def remove_conflicted_options(options, request):
|
||||
# type: (List[Option], Dict[str, str]) -> Tuple[List[str], List[str]]
|
||||
"""Takes our list of option tuples, and a cleaned copy of what was
|
||||
requested from getopt, and returns a copy of the request
|
||||
without any options that are marked as superseded, along with
|
||||
the list of superseded options
|
||||
"""
|
||||
def get_excluded_keys(memo, opt):
|
||||
return memo + ((len(opt) > 4 and opt[4]) or [])
|
||||
def get_excluded_keys(memo, option):
|
||||
return memo + option.conflicts
|
||||
|
||||
keys = request.keys()
|
||||
marked = [option for option in options if option[1] in keys]
|
||||
marked = [option for option in options if option.long in keys]
|
||||
exclude = reduce(get_excluded_keys, marked, [])
|
||||
excluded = [key for key in keys if key in exclude]
|
||||
cleaned = {key: request[key] for key in keys
|
||||
|
@ -66,12 +86,12 @@ def cleanup_options(options, commandline):
|
|||
return (cleaned, excluded)
|
||||
|
||||
def shortoptstogo(i):
|
||||
return i[0] + ((i[2] and ':') or '')
|
||||
return i.short + ((i.takes and ':') or '')
|
||||
|
||||
def longoptstogo(i):
|
||||
return i[1] + ((i[2] and '=') or '')
|
||||
return i.long + ((i.takes and '=') or '')
|
||||
|
||||
optstringsshort = ''.join([shortoptstogo(opt) for opt in options])
|
||||
optstringsshort = ''.join([shortoptstogo(opt) for opt in options if opt.short])
|
||||
optstringslong = [longoptstogo(opt) for opt in options]
|
||||
(chosen_options, filenames) = getopt.getopt(commandline[1:],
|
||||
optstringsshort,
|
||||
|
@ -84,4 +104,4 @@ def cleanup_options(options, commandline):
|
|||
(ret, excluded) = remove_conflicted_options(
|
||||
options, reduce(streamline_options, chosen_options, {}))
|
||||
|
||||
return (ret, filenames, excluded)
|
||||
return Arguments(ret, filenames, excluded)
|
||||
|
|
|
@ -1,39 +1,45 @@
|
|||
import gettext
|
||||
from option_handler import Option
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
OPTIONS = [
|
||||
('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).'), []),
|
||||
Option('o', 'only', True,
|
||||
_('A comma-separated list of only those linters to run'), ['exclude']),
|
||||
Option('x', 'exclude', True,
|
||||
_('A comma-separated list of linters to skip'), []),
|
||||
Option('l', 'linters', False,
|
||||
_('Show the list of configured linters'), []),
|
||||
Option('b', 'base', False,
|
||||
_('Check all changed files in the repository, not just those in the current directory.'), []),
|
||||
Option('a', 'all', False,
|
||||
_('Scan all files in the repository, not just those that have changed.'), ['revision']),
|
||||
Option('r', 'revision', True,
|
||||
_('Scan all files changed between revisions'), []),
|
||||
Option(None, 'pr', False,
|
||||
_('Scan all files changed between head and previous check-in'), ['revision']),
|
||||
Option('e', 'every', False,
|
||||
_('Short for -b -a: scan everything'), []),
|
||||
Option('w', 'workspace', False,
|
||||
_('Scan the workspace'), ['staging']),
|
||||
Option('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'), []),
|
||||
('t', 'bylinter', False,
|
||||
_('Group the reports by linter first as they appear in the config file [default]'), []),
|
||||
('f', 'byfile', False,
|
||||
_('Group the reports by file first'), []),
|
||||
('d', 'dryrun', False,
|
||||
_('Dry run - report what would be done, but do not run linters'), []),
|
||||
('c', 'config', True,
|
||||
_('Path to config file'), []),
|
||||
('h', 'help', False,
|
||||
_('This help message'), []),
|
||||
('V', 'verbose', False,
|
||||
_('A slightly more verbose output'), []),
|
||||
('v', 'version', False,
|
||||
_('Version information'), [])
|
||||
Option('t', 'bylinter', False,
|
||||
_('Group the reports by linter first as they appear in the config file [default]'), []),
|
||||
Option('f', 'byfile', False,
|
||||
_('Group the reports by file first'), []),
|
||||
Option('d', 'dryrun', False,
|
||||
_('Dry run - report what would be done, but do not run linters'), []),
|
||||
Option('c', 'config', True,
|
||||
_('Path to config file'), []),
|
||||
Option('h', 'help', False,
|
||||
_('This help message'), []),
|
||||
Option('V', 'verbose', False,
|
||||
_('A slightly more verbose output'), []),
|
||||
Option('v', 'version', False,
|
||||
_('Version information'), [])
|
||||
]
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
from __future__ import print_function
|
||||
from functools import reduce
|
||||
from .git_lint import load_config, run_linters, git_base
|
||||
import operator
|
||||
import gettext
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
def base_file_cleaner(files):
|
||||
return [file.replace(git_base + '/', '', 1) for file in files]
|
||||
|
||||
|
||||
# ICK. Mutation, references, and hidden assignment.
|
||||
def group_by(iterable, field_id):
|
||||
results = []
|
||||
keys = {}
|
||||
for obj in iterable:
|
||||
key = obj[field_id]
|
||||
if key in keys:
|
||||
keys[key].append(obj)
|
||||
continue
|
||||
keys[key] = [obj]
|
||||
results.append((key, keys[key]))
|
||||
return results
|
||||
|
||||
|
||||
def print_report(results, unlintable_filenames, cant_lint_filenames,
|
||||
broken_linter_names, unfindable_filenames, options={'bylinter': True}):
|
||||
|
||||
def base_file_cleaner(files):
|
||||
return [file.replace(git_base + '/', '', 1) for file in files]
|
||||
|
||||
# ICK. Mutation, references, and hidden assignment.
|
||||
def group_by(iterable, field_id):
|
||||
results = []
|
||||
keys = {}
|
||||
for obj in iterable:
|
||||
key = obj[field_id]
|
||||
if key in keys:
|
||||
keys[key].append(obj)
|
||||
continue
|
||||
keys[key] = [obj]
|
||||
results.append((key, keys[key]))
|
||||
return results
|
||||
|
||||
sort_position = 1
|
||||
grouping = _('Linter: {}')
|
||||
if 'byfile' in options:
|
||||
|
@ -44,9 +44,9 @@ def print_report(results, unlintable_filenames, cant_lint_filenames,
|
|||
print ('')
|
||||
|
||||
if len(broken_linter_names) and (len(cant_lint_filenames) or ('verbose' in options)):
|
||||
print(_('These linters could not be run:'), ','.join(broken_linter_names))
|
||||
print(_('Linters not found:'), ','.join(broken_linter_names))
|
||||
if len(cant_lint_filenames):
|
||||
print(_('Files not linted:'))
|
||||
print(' ' + _('Files not linted:'))
|
||||
print('\n'.join([' {}'.format(f) for f in cant_lint_filenames]))
|
||||
print('')
|
||||
|
||||
|
@ -56,7 +56,7 @@ def print_report(results, unlintable_filenames, cant_lint_filenames,
|
|||
print('')
|
||||
|
||||
if len(unfindable_filenames):
|
||||
print(_('Files not be found:'))
|
||||
print(_('Files not found:'))
|
||||
print('\n'.join([' {}'.format(f) for f in unfindable_filenames]))
|
||||
print('')
|
||||
|
||||
|
@ -64,7 +64,7 @@ def print_report(results, unlintable_filenames, cant_lint_filenames,
|
|||
def print_help(options, name):
|
||||
print(_('Usage: {} [options] [filenames]').format(name))
|
||||
for item in options:
|
||||
print(' -{:<1} --{:<12} {}'.format(item[0], item[1], item[3]))
|
||||
print(' {:<2} --{:<12} {}'.format((item[0] and ('-' + item[0])) or '', item[1], item[3]))
|
||||
|
||||
|
||||
def print_version(name, version):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[bumpversion]
|
||||
current_version = 0.0.4
|
||||
current_version = 0.0.7
|
||||
commit = True
|
||||
tag = True
|
||||
|
||||
|
@ -16,3 +16,4 @@ universal = 1
|
|||
|
||||
[flake8]
|
||||
exclude = docs
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -39,7 +39,7 @@ test_requirements = [
|
|||
|
||||
setup(
|
||||
name='git_linter',
|
||||
version='0.0.4',
|
||||
version='0.0.7',
|
||||
description="A git command to lint everything in your workspace (or stage) that was changed since the last commit.",
|
||||
long_description=readme + '\n\n' + history,
|
||||
author='Kenneth M. "Elf" Sternberg',
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
#!/usr/bin/hy ; -*- mode: clojure -*-
|
||||
(import subprocess os sys os.path)
|
||||
|
||||
(defn fmit [formatstring iterable]
|
||||
(apply .format (+ [formatstring] (list iterable))))
|
||||
|
||||
(defmain [&rest args]
|
||||
(let [[git_dir (subprocess.check_output ["git" "rev-parse" "--show-toplevel"])]]
|
||||
(.append sys.path (os.path.join git_dir "git_lint"))
|
||||
(import [git_lint [git_lint]])
|
||||
(print (.join "\n" (map (fn [i] (if (get i 2)
|
||||
(+ (fmit "\item[\oOptArg{{-{0}}}{{ names}} " i)
|
||||
(fmit "\oOptArg{{--{1}}}={{ names}}] " i)
|
||||
(fmit "{3}" i))
|
||||
(+ (fmit "\item[\oOptArg{{-{0}}} " i)
|
||||
(fmit "\oOptArg{{--{1}}}] " i)
|
||||
(fmit "{3}" i))))
|
||||
git_lint.OPTIONS_LIST)))))
|
||||
|
||||
|
Loading…
Reference in New Issue