Compare commits
1 Commits
master
...
inotify-ca
Author | SHA1 | Date |
---|---|---|
Elf M. Sternberg | 739075d2df |
|
@ -63,9 +63,3 @@ match = .json
|
|||
print = False
|
||||
condition = error
|
||||
|
||||
[readability]
|
||||
output = Running Text Style Check
|
||||
match = .rst
|
||||
command = git-lint-style --min=6 --max=12
|
||||
print = False
|
||||
condition = error
|
||||
|
|
|
@ -10,4 +10,4 @@ Development Lead
|
|||
Contributors
|
||||
------------
|
||||
|
||||
* Tino de Bruijn <work@tino.io>
|
||||
None yet. Why not be the first?
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
GitLint is a git command to automatically run a suite of pre-defined linters.
|
|
@ -8,6 +8,4 @@ recursive-include tests *
|
|||
recursive-exclude * __pycache__
|
||||
recursive-exclude * *.py[co]
|
||||
|
||||
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
|
||||
recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif
|
||||
|
|
10
Makefile
10
Makefile
|
@ -12,7 +12,6 @@ export BROWSER_PYSCRIPT
|
|||
BROWSER := python -c "$$BROWSER_PYSCRIPT"
|
||||
|
||||
help:
|
||||
@echo "all - local build including docs"
|
||||
@echo "clean - remove all build, test, coverage and Python artifacts"
|
||||
@echo "clean-build - remove build artifacts"
|
||||
@echo "clean-pyc - remove Python file artifacts"
|
||||
|
@ -22,14 +21,10 @@ help:
|
|||
@echo "test-all - run tests on every Python version with tox"
|
||||
@echo "coverage - check code coverage quickly with the default Python"
|
||||
@echo "docs - generate Sphinx HTML documentation, including API docs"
|
||||
@echo "docsbrowse - generate Sphinx HTML documentation and start browser"
|
||||
@echo "release - package and upload a release"
|
||||
@echo "dist - package"
|
||||
@echo "install - install the package to the active Python's site-packages"
|
||||
|
||||
all: docs
|
||||
python setup.py build
|
||||
|
||||
clean: clean-build clean-pyc clean-test
|
||||
|
||||
clean-build:
|
||||
|
@ -72,9 +67,6 @@ docs:
|
|||
sphinx-apidoc -o docs/ git_lint
|
||||
$(MAKE) -C docs clean
|
||||
$(MAKE) -C docs html
|
||||
$(MAKE) -C docs man
|
||||
|
||||
docbrowse: docs
|
||||
$(BROWSER) docs/_build/html/index.html
|
||||
|
||||
servedocs: docs
|
||||
|
@ -90,4 +82,4 @@ dist: clean
|
|||
ls -l dist
|
||||
|
||||
install: clean
|
||||
python setup.py install --prefix=/usr/local
|
||||
python setup.py install
|
||||
|
|
79
README.rst
79
README.rst
|
@ -1,66 +1,24 @@
|
|||
===============================
|
||||
Git Lint: README
|
||||
Git Lint
|
||||
===============================
|
||||
|
||||
A git command that automatically runs identifiable linters against
|
||||
changed files in your current git repository or staging area.
|
||||
changed files in current repository or staging.
|
||||
|
||||
* 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`-like
|
||||
command. Some commands may require shell wrappers.
|
||||
staging area. It can be configured to work with any `lint` 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`
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
--------
|
||||
|
||||
|
@ -79,22 +37,11 @@ Features
|
|||
restoration of your workspace, it ensures the timestamps are the
|
||||
same, so as not to confuse your build system or IDE.
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
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 <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.
|
||||
|
||||
The completion of this project was graciously sponsored by my employer,
|
||||
Splunk <http://splunk.com>.
|
||||
|
||||
Disclaimer
|
||||
----------
|
||||
|
|
|
@ -46,10 +46,6 @@ def main(*args):
|
|||
print(error)
|
||||
return returncode
|
||||
kincaid = re.search(r'Kincaid:\s*([\d\.]+)', values, re.MULTILINE)
|
||||
|
||||
# Assumes an uncheckable issue, like an empty RST. Not unusual.
|
||||
if not kincaid:
|
||||
return 0
|
||||
val = float(kincaid.group(1))
|
||||
|
||||
msg = ""
|
|
@ -1,6 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from git_lint.git_lint import load_config, run_linters, git_base
|
||||
from git_lint.reporters import print_report
|
||||
from git_lint import load_config, run_linters, git_base
|
||||
|
||||
import gettext
|
||||
_ = gettext.gettext
|
||||
|
@ -21,14 +20,14 @@ def main(*args):
|
|||
unlintable_filenames,
|
||||
cant_lint_filenames,
|
||||
broken_linter_names,
|
||||
unfindable_filenames) = run_linters(pre_commit_options, config)
|
||||
unfindable_filenames) = run_linters(options, config)
|
||||
|
||||
print_report(results,
|
||||
unlintable_filenames,
|
||||
cant_lint_filenames,
|
||||
broken_linter_names,
|
||||
unfindable_filenames,
|
||||
pre_commit_options)
|
||||
options)
|
||||
|
||||
if not len(results):
|
||||
return 0
|
||||
|
|
|
@ -14,19 +14,12 @@
|
|||
Scan the workspace [default]
|
||||
**-s, --staging**
|
||||
Scan the staging area (useful for pre-commit).
|
||||
**-c <path>, --config=<path>**
|
||||
Path to config file
|
||||
**-t, --bylinter**
|
||||
Group reports by linter first as they appear in the config file [default]
|
||||
**-f, --byfile**
|
||||
Group reports by file first, linter second
|
||||
**-c** <path>, --config**=<path> Path to config file
|
||||
**-d, --dryrun**
|
||||
Report what git-lint would do, but don't actually do anything.
|
||||
**-q, --quiet**
|
||||
Produce a short report of file that failed to pass.
|
||||
Produce a short report of files that failed to pass.
|
||||
**-h, --help**
|
||||
Print a short help message
|
||||
**-V, --verbose**
|
||||
Print a slightly more verbose long report
|
||||
**-v, --version**
|
||||
Print version information
|
||||
|
|
92
docs/conf.py
92
docs/conf.py
|
@ -20,7 +20,7 @@ import os
|
|||
# directory, add these directories to sys.path here. If the directory is
|
||||
# relative to the documentation root, use os.path.abspath to make it
|
||||
# absolute, like shown here.
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# Get the project root dir, which is the parent dir of this
|
||||
cwd = os.getcwd()
|
||||
|
@ -31,12 +31,12 @@ project_root = os.path.dirname(cwd)
|
|||
# version is used.
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
import git_lint # noqa
|
||||
import git_lint
|
||||
|
||||
# -- General configuration ---------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
# needs_sphinx = '1.0'
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
|
@ -49,7 +49,7 @@ templates_path = ['_templates']
|
|||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
@ -69,13 +69,13 @@ release = git_lint.__version__
|
|||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
# language = None
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to
|
||||
# some non-false value, then it is used:
|
||||
# today = ''
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
# today_fmt = '%B %d, %Y'
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
|
@ -83,60 +83,60 @@ exclude_patterns = ['_build']
|
|||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
# default_role = None
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
# add_function_parentheses = True
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
# add_module_names = True
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
# show_authors = False
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
#modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built
|
||||
# documents.
|
||||
# keep_warnings = False
|
||||
#keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'alabaster'
|
||||
html_theme = 'default'
|
||||
|
||||
# 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
|
||||
# documentation.
|
||||
# html_theme_options = {}
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
# html_title = None
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as
|
||||
# html_title.
|
||||
# html_short_title = None
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the
|
||||
# top of the sidebar.
|
||||
# html_logo = None
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon
|
||||
# of the docs. This file should be a Windows icon file (.ico) being
|
||||
# 16x16 or 32x32 pixels large.
|
||||
# html_favicon = None
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets)
|
||||
# here, relative to this directory. They are copied after the builtin
|
||||
|
@ -146,46 +146,46 @@ html_static_path = ['_static']
|
|||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page
|
||||
# bottom, using the given strftime format.
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
# html_use_smartypants = True
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
# html_sidebars = {}
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names
|
||||
# to template names.
|
||||
# html_additional_pages = {}
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
# html_domain_indices = True
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
# html_use_index = True
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
# html_split_index = False
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
# html_show_sourcelink = True
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer.
|
||||
# Default is True.
|
||||
# html_show_sphinx = True
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer.
|
||||
# Default is True.
|
||||
# html_show_copyright = True
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages
|
||||
# will contain a <link> tag referring to it. The value of this option
|
||||
# must be the base URL from which the finished HTML is served.
|
||||
# html_use_opensearch = ''
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
# html_file_suffix = None
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'git_lintdoc'
|
||||
|
@ -195,13 +195,13 @@ htmlhelp_basename = 'git_lintdoc'
|
|||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
# 'papersize': 'letterpaper',
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
# 'pointsize': '10pt',
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
# 'preamble': '',
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
|
@ -215,23 +215,23 @@ latex_documents = [
|
|||
|
||||
# The name of an image file (relative to this directory) to place at
|
||||
# the top of the title page.
|
||||
# latex_logo = None
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings
|
||||
# are parts, not chapters.
|
||||
# latex_use_parts = False
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
# latex_show_pagerefs = False
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# latex_show_urls = False
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# latex_appendices = []
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# latex_domain_indices = True
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ------------------------------------
|
||||
|
@ -245,7 +245,7 @@ man_pages = [
|
|||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# man_show_urls = False
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output ----------------------------------------
|
||||
|
@ -263,13 +263,13 @@ texinfo_documents = [
|
|||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# texinfo_appendices = []
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# texinfo_domain_indices = True
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
# texinfo_show_urls = 'footnote'
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
# texinfo_no_detailmenu = False
|
||||
#texinfo_no_detailmenu = False
|
||||
|
|
|
@ -12,30 +12,6 @@ git_lint.git_lint module
|
|||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
git_lint.option_handler module
|
||||
------------------------------
|
||||
|
||||
.. automodule:: git_lint.option_handler
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
git_lint.options module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: git_lint.options
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
git_lint.reporters module
|
||||
-------------------------
|
||||
|
||||
.. automodule:: git_lint.reporters
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
|
|
@ -3,12 +3,8 @@
|
|||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
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.
|
||||
Welcome to Git Lint's documentation!
|
||||
======================================
|
||||
|
||||
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_lint
|
||||
|
||||
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_lint
|
||||
|
||||
Or download the `tarball`_:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ curl -OL https://github.com/elfsternberg/git-linter/tarball/master
|
||||
$ curl -OL https://github.com/elfsternberg/git_lint/tarball/master
|
||||
|
||||
Once you have a copy of the source, you can install it with:
|
||||
|
||||
|
@ -44,8 +44,10 @@ 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_lint
|
||||
.. _tarball: https://github.com/elfsternberg/git_lint/tarball/master
|
||||
|
||||
Once installed, please copy the '.git-lint' example file. You may install this either in
|
||||
your home or repository directory as ``.git-lint``.
|
||||
Once installed, you may run the 'git lint --make-config' command, which
|
||||
will generate a simple configuration file. You may install this either
|
||||
in your home directory as ``.git-lint.conf`` or in your project's git
|
||||
directory as ``.git/lint/git-lint.conf``
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
=====
|
||||
Usage
|
||||
=====
|
||||
|
||||
|
||||
Command Line
|
||||
------------
|
||||
|
||||
|
@ -12,13 +12,24 @@ git lint [options] [filenames]
|
|||
Options
|
||||
-------
|
||||
|
||||
.. include:: arguments.rst
|
||||
.. include: arguments.rst
|
||||
|
||||
As a pre-commit hook:
|
||||
---------------------
|
||||
|
||||
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.
|
||||
.. code-block:: python
|
||||
|
||||
The pre-commit hook is *experimental*. Please be careful with it.
|
||||
#!/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.
|
||||
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
__author__ = 'Kenneth M. "Elf" Sternberg'
|
||||
__email__ = 'elf.sternberg@gmail.com'
|
||||
__version__ = '0.0.7'
|
||||
__version__ = '0.0.4'
|
||||
|
||||
__all__ = ['git_lint']
|
||||
|
|
|
@ -5,20 +5,75 @@ from .option_handler import cleanup_options
|
|||
from .reporters import print_report, print_help, print_linters
|
||||
from .git_lint import load_config, run_linters, git_base
|
||||
from getopt import GetoptError
|
||||
import os.path
|
||||
import sys
|
||||
import time
|
||||
|
||||
watchdog = False
|
||||
try:
|
||||
import watchdog
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import RegexMatchingEventHandler
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
import gettext
|
||||
_ = gettext.gettext
|
||||
|
||||
NAME = 'git-lint'
|
||||
VERSION = '0.0.7'
|
||||
VERSION = '0.0.4'
|
||||
|
||||
|
||||
def remove_unavailable_options(options):
|
||||
failures = [] + ((watchdog == False and ['monitor']) or [])
|
||||
return filter(lambda i: i[1] not in failures, options)
|
||||
|
||||
|
||||
def monitor(options, config, filenames):
|
||||
|
||||
observer = watchdog.observers.Observer()
|
||||
skip = ['\.git/']
|
||||
|
||||
def run_monitor_linters():
|
||||
(results,
|
||||
unlintable_filenames,
|
||||
cant_lint_filenames,
|
||||
broken_linter_names,
|
||||
unfindable_filenames) = run_linters(options, config, filenames)
|
||||
|
||||
print_report(results,
|
||||
unlintable_filenames,
|
||||
cant_lint_filenames,
|
||||
broken_linter_names,
|
||||
unfindable_filenames,
|
||||
options)
|
||||
|
||||
class LintMonitor(RegexMatchingEventHandler):
|
||||
def __init__(self):
|
||||
super(LintMonitor, self).__init__(ignore_regexes=skip)
|
||||
|
||||
def on_created(self, event):
|
||||
run_monitor_linters()
|
||||
|
||||
def on_modified(self, event):
|
||||
run_monitor_linters()
|
||||
|
||||
observer.schedule(LintMonitor(), git_base, recursive=True)
|
||||
observer.start()
|
||||
try:
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
observer.stop()
|
||||
observer.join()
|
||||
|
||||
|
||||
def main():
|
||||
if git_base is None:
|
||||
sys.exit(_('A git repository was not found.'))
|
||||
|
||||
(options, filenames, excluded_commands) = cleanup_options(OPTIONS, sys.argv)
|
||||
initial_options = remove_unavailable_options(OPTIONS)
|
||||
(options, filenames, excluded_commands) = cleanup_options(initial_options, sys.argv)
|
||||
|
||||
if len(excluded_commands) > 0:
|
||||
print(_('These command line options were ignored due to option precedence.'))
|
||||
|
@ -29,7 +84,7 @@ def main():
|
|||
config = load_config(options, git_base)
|
||||
|
||||
if 'help' in options:
|
||||
print_help(OPTIONS, NAME)
|
||||
print_help(initial_options, NAME)
|
||||
return 0
|
||||
|
||||
if 'version' in options:
|
||||
|
@ -43,6 +98,9 @@ def main():
|
|||
print_linters(config, broken_linter_names)
|
||||
return 0
|
||||
|
||||
if 'monitor' in options:
|
||||
return monitor(options, config, filenames)
|
||||
|
||||
(results,
|
||||
unlintable_filenames,
|
||||
cant_lint_filenames,
|
||||
|
@ -62,7 +120,7 @@ def main():
|
|||
return max([i[2] for i in results if len(i)])
|
||||
|
||||
except GetoptError as err:
|
||||
print_help(OPTIONS)
|
||||
print_help(initial_options)
|
||||
return 1
|
||||
|
||||
|
||||
|
|
|
@ -9,18 +9,11 @@ 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
|
||||
|
||||
|
||||
|
@ -31,6 +24,39 @@ _ = 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.
|
||||
|
@ -44,38 +70,6 @@ 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()
|
||||
|
@ -152,6 +146,10 @@ git_head = get_git_head()
|
|||
# \___/ \__|_|_|_|\__|_\___/__/
|
||||
#
|
||||
|
||||
def base_file_cleaner(files):
|
||||
return [file.replace(git_base + '/', '', 1) for file in files]
|
||||
|
||||
|
||||
class MatchFilter:
|
||||
|
||||
def __init__(self, config):
|
||||
|
@ -174,34 +172,37 @@ class MatchFilter:
|
|||
# \___|_||_\___\__|_\_\ |_|_|_||_\__\___|_| /__/
|
||||
#
|
||||
|
||||
def linter_exists(linter, label):
|
||||
if not len(linter):
|
||||
sys.exit(_('Syntax error in linter configuration for {} ').format(label))
|
||||
def executable_exists(script, label):
|
||||
if not len(script):
|
||||
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))
|
||||
scriptname = script.split(' ').pop(0)
|
||||
if not len(scriptname):
|
||||
sys.exit(
|
||||
_('Syntax error in command configuration for {} ').format(label))
|
||||
|
||||
def is_executable(path):
|
||||
return os.path.exists(path) and os.access(path, os.X_OK)
|
||||
|
||||
if lintername.startswith('/'):
|
||||
return (is_executable(lintername) and lintername) or None
|
||||
if scriptname.startswith('/'):
|
||||
return (is_executable(scriptname) and scriptname) or None
|
||||
|
||||
# shutil.which() doesn't appear until Python 3, darnit.
|
||||
possibles = [path for path in
|
||||
[os.path.join(path, lintername)
|
||||
[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 False
|
||||
|
||||
|
||||
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)]
|
||||
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):
|
||||
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
|
||||
|
@ -275,11 +276,6 @@ 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 """
|
||||
|
||||
|
@ -311,9 +307,6 @@ 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:
|
||||
|
@ -329,33 +322,33 @@ def get_filelist(options, extras):
|
|||
# |___/ |___/ |_| |_|
|
||||
|
||||
|
||||
class StagingRunner:
|
||||
def __init__(self, filenames):
|
||||
self.filenames = filenames
|
||||
class Runner:
|
||||
def __init__(self, options):
|
||||
self.runner = ('staging' in options and Runner.staging_wrapper) or Runner.workspace_wrapper
|
||||
|
||||
def __enter__(self):
|
||||
def __call__(self, run_linters, filenames):
|
||||
return self.runner(run_linters, filenames)
|
||||
|
||||
@staticmethod
|
||||
def staging_wrapper(run_linters, filenames):
|
||||
def time_gather(f):
|
||||
stats = os.stat(f)
|
||||
return (f, (stats.st_atime, stats.st_mtime))
|
||||
self.times = [time_gather(filename) for filename in self.filenames]
|
||||
|
||||
times = [time_gather(file) for file in filenames]
|
||||
run_git_command(['stash', '--keep-index'])
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
results = run_linters()
|
||||
run_git_command(['reset', '--hard'])
|
||||
run_git_command(['stash', 'pop', '--quiet', '--index'])
|
||||
for (filename, timepair) in self.times:
|
||||
|
||||
for (filename, timepair) in times:
|
||||
os.utime(filename, timepair)
|
||||
return results
|
||||
|
||||
|
||||
class WorkspaceRunner(object):
|
||||
def __init__(self, filenames):
|
||||
pass
|
||||
|
||||
def __enter__(self):
|
||||
pass
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
pass
|
||||
@staticmethod
|
||||
def workspace_wrapper(run_linters, filenames):
|
||||
return run_linters()
|
||||
|
||||
|
||||
# ___ _ _ _
|
||||
|
@ -379,7 +372,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, otherwise return nothing.
|
||||
return the error code and messages, either return nothing.
|
||||
"""
|
||||
|
||||
cmd = linter['command'] + ' "' + filename + '"'
|
||||
|
@ -432,9 +425,6 @@ 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 """
|
||||
|
@ -444,31 +434,21 @@ 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)]
|
||||
|
||||
runner = WorkspaceRunner
|
||||
if 'staging' in options:
|
||||
runner = StagingRunner
|
||||
runner = Runner(options)
|
||||
|
||||
linters = Linters(build_config_subset(working_linter_names),
|
||||
sorted(lintable_filenames))
|
||||
|
@ -478,8 +458,7 @@ def run_linters(options, config, extras=[]):
|
|||
return (dryrun_results, unlintable_filenames, cant_lint_filenames,
|
||||
broken_linter_names, unfindable_filenames)
|
||||
|
||||
with runner(lintable_filenames):
|
||||
results = linters()
|
||||
results = runner(linters, lintable_filenames)
|
||||
|
||||
return (results, unlintable_filenames, cant_lint_filenames,
|
||||
broken_linter_names, unfindable_filenames)
|
||||
|
|
|
@ -2,18 +2,8 @@
|
|||
# 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
|
||||
|
@ -27,7 +17,6 @@ Arguments = namedtuple('Arguments', ['arguments', 'filenames', 'excluded']) # t
|
|||
|
||||
|
||||
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.
|
||||
|
@ -35,67 +24,56 @@ 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 = {} # type: Dict[str, str]
|
||||
fullset = {}
|
||||
for option in options:
|
||||
if option.long:
|
||||
fullset['--' + option.long] = option.long
|
||||
if option.short:
|
||||
fullset['-' + option.short] = option.long
|
||||
if option[1]:
|
||||
fullset['--' + option[1]] = option[1]
|
||||
if option[0]:
|
||||
fullset['-' + option[0]] = option[1]
|
||||
|
||||
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, option):
|
||||
return memo + option.conflicts
|
||||
def get_excluded_keys(memo, opt):
|
||||
return memo + ((len(opt) > 4 and opt[4]) or [])
|
||||
|
||||
keys = request.keys()
|
||||
marked = [option for option in options if option.long in keys]
|
||||
marked = [option for option in options if option[1] 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
|
||||
if key not in excluded}
|
||||
return (cleaned, excluded)
|
||||
|
||||
def shortoptstogo(i):
|
||||
return i.short + ((i.takes and ':') or '')
|
||||
def shortoptstogo(i): return i[0] + ((i[2] and ':') or '')
|
||||
|
||||
def longoptstogo(i):
|
||||
return i.long + ((i.takes and '=') or '')
|
||||
def longoptstogo(i): return i[1] + ((i[2] and '=') or '')
|
||||
|
||||
optstringsshort = ''.join([shortoptstogo(opt) for opt in options if opt.short])
|
||||
optstringsshort = ''.join([shortoptstogo(opt) for opt in options])
|
||||
optstringslong = [longoptstogo(opt) for opt in options]
|
||||
(chosen_options, filenames) = getopt.getopt(commandline[1:],
|
||||
optstringsshort,
|
||||
optstringslong)
|
||||
optstringsshort,
|
||||
optstringslong)
|
||||
|
||||
# Turns what getopt returns into something more human-readable
|
||||
streamline_options = make_option_streamliner(options)
|
||||
|
@ -104,4 +82,4 @@ def cleanup_options(options, commandline):
|
|||
(ret, excluded) = remove_conflicted_options(
|
||||
options, reduce(streamline_options, chosen_options, {}))
|
||||
|
||||
return Arguments(ret, filenames, excluded)
|
||||
return (ret, filenames, excluded)
|
||||
|
|
|
@ -1,45 +1,40 @@
|
|||
import gettext
|
||||
from option_handler import Option
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
OPTIONS = [
|
||||
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'), []),
|
||||
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'), [])
|
||||
('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).'), ['monitor']),
|
||||
('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'), []),
|
||||
('m', 'monitor', False,
|
||||
_('Run continuously, monitoring filesytem for changes'), []),
|
||||
('c', 'config', True,
|
||||
_('Path to config file'), []),
|
||||
('h', 'help', False,
|
||||
_('This help message'), []),
|
||||
('v', 'version', False,
|
||||
_('Version information'), [])
|
||||
]
|
||||
|
||||
|
|
|
@ -1,70 +1,52 @@
|
|||
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
|
||||
|
||||
|
||||
# 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:
|
||||
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]:
|
||||
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(_('Linters not found:'), ','.join(broken_linter_names))
|
||||
print('\n'.join(text))
|
||||
print('')
|
||||
if len(broken_linter_names):
|
||||
print(_('These linters could not be run:'), ','.join(broken_linter_names))
|
||||
if len(cant_lint_filenames):
|
||||
print(' ' + _('Files not linted:'))
|
||||
print(_('As a result, these files were not linted:'))
|
||||
print('\n'.join([' {}'.format(f) for f in cant_lint_filenames]))
|
||||
print('')
|
||||
|
||||
if len(unlintable_filenames) and ('verbose' in options):
|
||||
print(_('No recognizeable linters for:'))
|
||||
if len(unlintable_filenames):
|
||||
print(_('The following files had no recognizeable linters:'))
|
||||
print('\n'.join([' {}'.format(f) for f in unlintable_filenames]))
|
||||
print('')
|
||||
|
||||
if len(unfindable_filenames):
|
||||
print(_('Files not found:'))
|
||||
print(_('The following files could not be found:'))
|
||||
print('\n'.join([' {}'.format(f) for f in unfindable_filenames]))
|
||||
print('')
|
||||
|
||||
|
||||
def print_help(options, name):
|
||||
print(_('Usage: {} [options] [filenames]').format(name))
|
||||
for item in options:
|
||||
print(' {:<2} --{:<12} {}'.format((item[0] and ('-' + item[0])) or '', item[1], item[3]))
|
||||
print(' -{:<1} --{:<12} {}'.format(item[0], item[1], item[3]))
|
||||
|
||||
|
||||
def print_version(name, version):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[bumpversion]
|
||||
current_version = 0.0.7
|
||||
current_version = 0.0.4
|
||||
commit = True
|
||||
tag = True
|
||||
|
||||
|
@ -16,4 +16,3 @@ universal = 1
|
|||
|
||||
[flake8]
|
||||
exclude = docs
|
||||
|
||||
|
|
21
setup.py
21
setup.py
|
@ -1,22 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
import re
|
||||
import sys
|
||||
import argparse
|
||||
import os.path
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
def get_data_files(prefix):
|
||||
if prefix.startswith('/System/Library/Frameworks'):
|
||||
return []
|
||||
return [(os.path.join(prefix, 'share/man/man1'), ['docs/_build/man/git-lint.1'])]
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--prefix', default='',
|
||||
help='prefix to install data files')
|
||||
opts, _ = parser.parse_known_args(sys.argv)
|
||||
prefix = opts.prefix or sys.prefix or '/usr'
|
||||
|
||||
try:
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
|
@ -39,7 +24,7 @@ test_requirements = [
|
|||
|
||||
setup(
|
||||
name='git_linter',
|
||||
version='0.0.7',
|
||||
version='0.0.4',
|
||||
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',
|
||||
|
@ -51,11 +36,9 @@ setup(
|
|||
package_dir={'git_lint':
|
||||
'git_lint'},
|
||||
include_package_data=True,
|
||||
data_files=get_data_files(prefix),
|
||||
install_requires=requirements,
|
||||
license="MIT",
|
||||
zip_safe=False,
|
||||
scripts=['bin/git-lint-style'],
|
||||
keywords='git lint style syntaxt development',
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
|
|
|
@ -102,17 +102,6 @@ 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)
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#!/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