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:
Elf M. Sternberg 2016-10-03 12:13:35 -07:00
parent aa4b5aa978
commit 13b0378b67
5 changed files with 97 additions and 37 deletions

View File

@ -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
----------

View File

@ -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)

View File

@ -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'), [])
]

View File

@ -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):

View File

@ -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)