Fixed reportage matrix.
Now it doesn't print things the user doesn't care about, like empty lint passes, or broken linters that aren't relevant to the current project. Added a test pass to ensure it doesn't blow up when running against an empty stage.
This commit is contained in:
parent
aa4b5aa978
commit
13b0378b67
57
README.rst
57
README.rst
|
@ -1,23 +1,49 @@
|
|||
===============================
|
||||
Git Lint
|
||||
Git Lint: README
|
||||
===============================
|
||||
|
||||
A git command that automatically runs identifiable linters against
|
||||
changed files in current repository or staging.
|
||||
changed files in your current git repository or staging area.
|
||||
|
||||
* Free software: MIT license
|
||||
|
||||
**Git Lint** runs a configurable set of syntax, style, and complexity
|
||||
checkers against changed files in your current working directory or
|
||||
staging area. It can be configured to work with any `lint` command.
|
||||
Some commands may require shell wrappers.
|
||||
staging area. It can be configured to work with any `lint`-like
|
||||
command. Some commands may require shell wrappers.
|
||||
|
||||
While it may be possible to create a custom lint command in your npm,
|
||||
grunt, Make, CMake, or whatever, the fact is we all use a VCS, and
|
||||
most of us use git. Having a centralized repository for what we want
|
||||
checked and how we want it checked, associated with git (and by
|
||||
extension, as a pre-commit hook), that can be run at any time for any
|
||||
reason.
|
||||
grunt, Make, CMake, or whatever, the fact is we all use a VCS, and most
|
||||
of us use git. Having a centralized repository for what we want checked
|
||||
and how we want it checked, associated with git (and by extension, as a
|
||||
pre-commit hook), that can be run at any time for any reason.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
To lint only what's changed recently in your current working directory:
|
||||
`git lint`
|
||||
|
||||
To lint everything, changed or otherwise, from the current directory down:
|
||||
`git lint -a`
|
||||
|
||||
To lint what's changed from the repo's base:
|
||||
`git lint -b`
|
||||
|
||||
To lint what's in your staging directory:
|
||||
`git lint -s`
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
`pip install git-linter`
|
||||
|
||||
You will need to copy the .git-lint configuration file to either your
|
||||
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.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
@ -37,11 +63,16 @@ Features
|
|||
restoration of your workspace, it ensures the timestamps are the
|
||||
same, so as not to confuse your build system or IDE.
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
The completion of this project was graciously sponsored by my employer,
|
||||
Splunk <http://splunk.com>.
|
||||
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
|
||||
|
||||
|
||||
Disclaimer
|
||||
----------
|
||||
|
|
|
@ -318,33 +318,33 @@ def get_filelist(options, extras):
|
|||
# |___/ |___/ |_| |_|
|
||||
|
||||
|
||||
class Runner:
|
||||
def __init__(self, options):
|
||||
self.runner = ('staging' in options and Runner.staging_wrapper) or Runner.workspace_wrapper
|
||||
class StagingRunner:
|
||||
def __init__(self, filenames):
|
||||
self.filenames = filenames
|
||||
|
||||
def __call__(self, run_linters, filenames):
|
||||
return self.runner(run_linters, filenames)
|
||||
|
||||
@staticmethod
|
||||
def staging_wrapper(run_linters, filenames):
|
||||
def __enter__(self):
|
||||
def time_gather(f):
|
||||
stats = os.stat(f)
|
||||
return (f, (stats.st_atime, stats.st_mtime))
|
||||
|
||||
times = [time_gather(file) for file in filenames]
|
||||
self.times = [time_gather(filename) for filename in self.filenames]
|
||||
run_git_command(['stash', '--keep-index'])
|
||||
|
||||
results = run_linters()
|
||||
def __exit__(self, type, value, traceback):
|
||||
run_git_command(['reset', '--hard'])
|
||||
run_git_command(['stash', 'pop', '--quiet', '--index'])
|
||||
|
||||
for (filename, timepair) in times:
|
||||
for (filename, timepair) in self.times:
|
||||
os.utime(filename, timepair)
|
||||
return results
|
||||
|
||||
@staticmethod
|
||||
def workspace_wrapper(run_linters, filenames):
|
||||
return run_linters()
|
||||
|
||||
class WorkspaceRunner(object):
|
||||
def __init__(self, filenames):
|
||||
pass
|
||||
|
||||
def __enter__(self):
|
||||
pass
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
pass
|
||||
|
||||
|
||||
# ___ _ _ _
|
||||
|
@ -368,7 +368,7 @@ class Linters:
|
|||
"""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.
|
||||
return the error code and messages, otherwise return nothing.
|
||||
"""
|
||||
|
||||
cmd = linter['command'] + ' "' + filename + '"'
|
||||
|
@ -444,7 +444,9 @@ def run_linters(options, config, extras=[]):
|
|||
cant_lint_filenames = [filename for filename in lintable_filenames
|
||||
if cant_lint_filter(filename)]
|
||||
|
||||
runner = Runner(options)
|
||||
runner = WorkspaceRunner
|
||||
if 'staging' in options:
|
||||
runner = StagingRunner
|
||||
|
||||
linters = Linters(build_config_subset(working_linter_names),
|
||||
sorted(lintable_filenames))
|
||||
|
@ -454,7 +456,8 @@ def run_linters(options, config, extras=[]):
|
|||
return (dryrun_results, unlintable_filenames, cant_lint_filenames,
|
||||
broken_linter_names, unfindable_filenames)
|
||||
|
||||
results = runner(linters, lintable_filenames)
|
||||
with runner(lintable_filenames):
|
||||
results = linters()
|
||||
|
||||
return (results, unlintable_filenames, cant_lint_filenames,
|
||||
broken_linter_names, unfindable_filenames)
|
||||
|
|
|
@ -32,6 +32,8 @@ OPTIONS = [
|
|||
_('Path to config file'), []),
|
||||
('h', 'help', False,
|
||||
_('This help message'), []),
|
||||
('V', 'verbose', False,
|
||||
_('A slightly more verbose output'), []),
|
||||
('v', 'version', False,
|
||||
_('Version information'), [])
|
||||
]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import print_function
|
||||
from .git_lint import load_config, run_linters, git_base
|
||||
import operator
|
||||
import gettext
|
||||
_ = gettext.gettext
|
||||
|
||||
|
@ -30,22 +31,34 @@ def print_report(results, unlintable_filenames, cant_lint_filenames,
|
|||
sort_position = 0
|
||||
grouping = _('Filename: {}')
|
||||
grouped_results = group_by(results, sort_position)
|
||||
|
||||
for group in grouped_results:
|
||||
messages = reduce(operator.add, [item[3] for item in group[1]], [])
|
||||
if len(messages) == 0:
|
||||
continue
|
||||
print(grouping.format(group[0]))
|
||||
for (filename, lintername, returncode, text) in group[1]:
|
||||
print('\n'.join(base_file_cleaner(text)))
|
||||
print('')
|
||||
if len(broken_linter_names):
|
||||
if text:
|
||||
print('\n'.join(base_file_cleaner(text)))
|
||||
print('')
|
||||
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))
|
||||
if len(cant_lint_filenames):
|
||||
print(_('As a result, these files were not linted:'))
|
||||
print('\n'.join([' {}'.format(f) for f in cant_lint_filenames]))
|
||||
if len(unlintable_filenames):
|
||||
print('')
|
||||
|
||||
if len(unlintable_filenames) and ('verbose' in options):
|
||||
print(_('The following files had no recognizeable linters:'))
|
||||
print('\n'.join([' {}'.format(f) for f in unlintable_filenames]))
|
||||
print('')
|
||||
|
||||
if len(unfindable_filenames):
|
||||
print(_('The following files could not be found:'))
|
||||
print('\n'.join([' {}'.format(f) for f in unfindable_filenames]))
|
||||
print('')
|
||||
|
||||
|
||||
def print_help(options, name):
|
||||
|
|
|
@ -102,6 +102,17 @@ def test_02_empty_repository():
|
|||
assert stderr.startswith('No configuration file found,')
|
||||
|
||||
|
||||
# It should behave well when the repository is empty and we're
|
||||
# running against staging.
|
||||
|
||||
def test_02b_empty_repository():
|
||||
with gittemp() as path:
|
||||
os.chdir(path)
|
||||
shell('git init')
|
||||
(stdout, stderr, rc) = fullshell('git lint -s')
|
||||
assert stderr.startswith('No configuration file found,')
|
||||
|
||||
|
||||
def test_03_simple_repository():
|
||||
with gittemp() as path:
|
||||
os.chdir(path)
|
||||
|
|
Loading…
Reference in New Issue