Trying to restructure this to be more sensible.

This commit is contained in:
Elf M. Sternberg 2016-09-28 10:01:02 -07:00
parent b75b4efe7e
commit 33c3d7a189
9 changed files with 214 additions and 290 deletions

25
docs/arguments.rst Normal file
View File

@ -0,0 +1,25 @@
**-o <linters>, --only=<linters>**
A comma-separated list of only those linters to run.
**-x <linters> --exclude=<linters>**
A comma-separated list of linters to skip.
**-l, --linters**
Show the list of configured linters.
**-b, --base**
Check all changed files from GIT_DIR, not just those in the current directory and down.
**-a, --all**
Scan all files, not just those that have changed.
**-e, --every**
Scan all files, not just those that have changed, from GIT_DIR. Short for -b -a
**-w, --workspace**
Scan the workspace [default]
**-s, --staging**
Scan the staging area (useful for pre-commit).
**-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 files that failed to pass.
**-h, --help**
Print a short help message
**-v, --version**
Print version information

View File

@ -239,7 +239,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'git_lint',
('manual', 'git-lint',
u'Git Lint Documentation',
[u'Kenneth M. "Elf" Sternberg'], 1)
]

View File

@ -1,143 +0,0 @@
\documentclass[english]{article}
\usepackage[latin1]{inputenc}
\usepackage{babel}
\usepackage{verbatim}
%% do we have the `hyperref package?
\IfFileExists{hyperref.sty}{
\usepackage[bookmarksopen,bookmarksnumbered]{hyperref}
}{}
%% do we have the `fancyhdr' package?
\IfFileExists{fancyhdr.sty}{
\usepackage[fancyhdr]{latex2man}
}{
%% do we have the `fancyheadings' package?
\IfFileExists{fancyheadings.sty}{
\usepackage[fancy]{latex2man}
}{
\usepackage[nofancy]{latex2man}
\message{no fancyhdr or fancyheadings package present, discard it}
}}
\setDate{2016/09/26} %%%% must be manually set, if rcsinfo is not present
\setVersionWord{Version:} %%% that's the default, no need to set it.
\setVersion{0.4}
\begin{document}
\begin{Name}{1}{git-lint}{Elf M. Sternberg}{Utilities}{Git Lint - A smart lint wrapper around your git repository}
\Prog{Git Lint} - A smart wrapper around your git repository
\end{Name}
\section{Synopsis}
%%%%%%%%%%%%%%%%%%
\Prog{git lint} [<options>..] [filenames..]
\section{Description}
%%%%%%%%%%%%%%%%%%%%%
\Prog{git-lint} provides a simple, single call to perform syntatic and stylistic
checks of your repository, in order to ensure they comply with your standards
before you commit your work.
\section{Options}
%%%%%%%%%%%%%%%%%
\begin{Description}
\item[\OptArg{-o}{ linters}]Specify the list of linters to run, excluding all others
\item[\OptArg{-x}{ linters}]Specify the list of linters to exclude, running all others
\item[\Opt{-l}]List configured linters. The list will specify whether or not the
command line argument provided leads to an identifiable executable linter.
\item[\Opt{-b}]Run check from the repository base, rather than the current working directory.
\item[\Opt{-a}]Check all files, not just those that have changed.
\item[\Opt{-e}]Check everything (short for {-a -b}).
\item[\Opt{-w}]Check the workspace (default when run as \Prog{git-lint}).
\item[\Opt{-s}]Check the staging area. \Prog{git-lint} stashes the
current workspace, restoring it to the same state as the index.
After the check, \Prog{git-lint} restores the workspace and attempts
to reset all fill times correctly.
\item[\Opt{-g}]Only error if lint failures overlap diffed regions.
\item[\Opt{-p}]Error if a lint failure happens anywhere in a checked file.
\item[\Opt{-t}]Group output by linter first, then filenames [default].
\item[\Opt{-f}]Group output by filenames first, then linter.
\item[\Opt{-d}]Dryrun - Report what \Prog{git-lint} would do, but don't actually run linters.
\item[\OptArg{-c}{ config-file}]Specify an alternative configuration file.
\item[\Opt{-h}]Print short help message
\item[\Opt{-v}]Print version information
\end{Description}
\section{Requirements}
%%%%%%%%%%%%%%%%%%%%%%
\begin{description}\setlength{\itemsep}{0cm}
\item[An IBM or Lenovo Thinkpad with HDAPS] \Prog{thinksaber} only
runs on laptops with accelerometers, which get their values through
the HDAPS joystick emulator.
\item[PyGame] \Prog{thinksaber} uses the PyGame library
(www.pygame.org), which in turn has dependencies on the Simple
Direct Layer gaming library as well as Python. Most Linux
distributions either come with this stock or provide it through the
installation tool. Pygame is a dependency of a number of popular
Linux games, so if you have any games on your system it's entirely
likely this has already been done for you.
\item[Make] If you want to install the system with the distributed
\File{Makefile}, you need GNU-\Prog{make}. If you don't have it, you
should execute the steps shown in the \File{Makefile} manually.
\end{description}
\section{Acknowledgements}
%%%%%%%%%%%%%%%%%%%%%%
\Prog{Thinksaber} is obviously inspired by the program MacSaber, and I'm
grateful to the MacSaber people for assembling the Star Wars sound
effects collection needed to make it so successful.
\Prog{Thinksaber} uses a motion-detection algorithm derived from the
one written by Tatsuhiko Miyagawa (miyagawa at gmail.com) for his own
\Prog{thinkpad-saber} program, which ran only under Perl for Windows.
Obviously, I think mine's better.
\section{Changes}
%@% IF LATEX %@%
{\small\verbatiminput{CHANGES}}
%@% ELSE %@%
Please check the file \URL{CHANGES} for the list of changes.
%@% END-IF %@%
\section{Version}
%%%%%%%%%%%%%%%%%
Version: \Version\ of \Date.
\section{License and Copyright}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{description}
\item[Copyright] \copyright\ 2008, Elf M. Sternberg,
\Email{Elf.Sternberg@gmail.com}
\item[License] This program can be redistributed and/or modified under
the terms of the GNU Public License, version 2. You should have
found a copy of this licence with this distribution in the file
\File{COPYING}.
\end{description}
\section{Author}
%%%%%%%%%%%%%%%%
\noindent
Elf M. Sternberg
Email: \Email{Elf.Sternberg@gmail.com} \\
WWW: \URL{http://www.elfsternberg.com}.
\LatexManEnd
\end{document}

56
docs/manual.rst Normal file
View File

@ -0,0 +1,56 @@
git-lint(1)
===========
NAME
----
git-lint - Run configured linters against changed files
SYNOPSIS
--------
[verse]
``git lint`` [<options>...] [<files...>]
DESCRIPTION
-----------
Runs a list of configured linters against a specified list of files in
your repository. By default all linters will be run against the
changed files in your current workspace, from the current working
directory on down. Command line options let you choose a different
directory, a different of files, the complete set of files, and even
the files currently in the staging area.
OPTIONS
-------
.. include:: arguments.rst
OUTPUT
------
By default, the output is that of all the linters specified, in the
order in which they appear in the configuration file, followed by
every file specified, sorted ASCIIbetically. This order can be
flipped (files first, then linters) with the ``--byfiles`` option.
``git lint`` returns the maximal error code if any linters fail a
pass, or zero if they all succeed.
CONFIGURATION
-------------
``git lint`` uses a standard INI-style configuration file. Aside from the
DEFAULT section, the name of each section is an alphanumeric token name for
a linter, followed by configuration details for that linter. Standard details
are:
* output - Text to print before running a linter.
* command - The actual command to run, minus the file path
* match - A comma-separated list of extensions to match against the linter
* print - If true, will prefix each line of output from the linter with the filename
* condition - if "error", the return code of the linter is the status of the pass. If "output," any output will result in a failure.
* comment - Text to include when running the ``--linters`` option

View File

@ -11,36 +11,8 @@ git lint [options] [filenames]
Options
-------
``-o`` ``--only``
A comma-separated list of only those linters to run
``-x`` ``--exclude``
A comma-separated list of linters to skip
``-l`` ``--linters``
Show the list of configured linters
``-b`` ``--base``
Check all changed files in the repository, not just those in the current directory.
``-a`` ``--all``
Scan all files in the repository, not just those that have changed.
``-e`` ``--every``
Short for -b -a: scan everything
``-w`` ``--workspace``
Scan the workspace
``-s`` ``--staging``
Scan the staging area (useful for pre-commit).
``-g`` ``--changes``
Report lint failures only for diff'd sections
``-p`` ``--complete``
Report lint failures for all files
``-c`` ``--config``
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 files that failed to pass.
``-h`` ``--help``
This help message
``-v`` ``--version``
Version information
.. include: arguments.rst
As a pre-commit hook:
---------------------

View File

@ -2,4 +2,6 @@
__author__ = 'Kenneth M. "Elf" Sternberg'
__email__ = 'elf.sternberg@gmail.com'
__version__ = '0.0.2'
__version__ = '0.0.4'
__all__ = ['git_lint']

View File

@ -19,40 +19,7 @@ _ = gettext.gettext
VERSION = '0.0.4'
NAME = 'git-lint'
OPTIONS_LIST = [
('o', 'only', True,
_('A comma-separated list of only those linters to run'), ['exclude']),
('x', 'exclude', True,
_('A comma-separated list of linters to skip'), []),
('l', 'linters', False,
_('Show the list of configured linters'), []),
('b', 'base', False,
_('Check all changed files in the repository, not just those in the current directory.'), []),
('a', 'all', False,
_('Scan all files in the repository, not just those that have changed.'), []),
('e', 'every', False,
_('Short for -b -a: scan everything'), []),
('w', 'workspace', False,
_('Scan the workspace'), ['staging']),
('s', 'staging', False,
_('Scan the staging area (useful for pre-commit).'), []),
('g', 'changes', False,
_("Report lint failures only for diff'd sections"), ['complete']),
('p', 'complete', False,
_('Report lint failures for all files'), []),
('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', 'version', False,
_('Version information'), [])
]
# ___ _ _ _
# / __|___ _ __ _ __ __ _ _ _ __| | | | (_)_ _ ___
@ -61,74 +28,6 @@ OPTIONS_LIST = [
#
# This was a lot shorter and smarter in Hy...
def make_rational_options(optlist, args):
# OptionTupleList -> (getOptOptions -> dictionaryOfOptions)
def make_options_rationalizer(optlist):
"""Takes a list of option tuples, and returns a function that takes
the output of getopt and reduces it to the longopt key and
associated values as a dictionary.
"""
def make_opt_assoc(prefix, pos):
def associater(acc, it):
acc[(prefix + it[pos])] = it[1]
return acc
return associater
short_opt_assoc = make_opt_assoc('-', 0)
long_opt_assoc = make_opt_assoc('--', 1)
def make_full_set(acc, i):
return long_opt_assoc(short_opt_assoc(acc, i), i)
fullset = reduce(make_full_set, optlist, {})
def rationalizer(acc, it):
acc[fullset[it[0]]] = it[1]
return acc
return rationalizer
# (OptionTupleList, dictionaryOfOptions) -> (dictionaryOfOptions, excludedOptions)
def remove_conflicted_options(optlist, request):
"""Takes our list of option tuples, and a cleaned copy of what was
requested from getopt, and returns a copy of the request
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 [])
keys = request.keys()
marked = [option for option in optlist 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[0] + ((i[2] and ':') or '')
def longoptstogo(i):
return i[1] + ((i[2] and '=') or '')
optstringsshort = ''.join([shortoptstogo(opt) for opt in optlist])
optstringslong = [longoptstogo(opt) for opt in optlist]
(options, filenames) = getopt.getopt(args[1:], optstringsshort,
optstringslong)
# Turns what getopt returns into something more human-readable
rationalize_options = make_options_rationalizer(optlist)
# Remove any options that are superseded by others.
(retoptions, excluded) = remove_conflicted_options(
optlist, reduce(rationalize_options, options, {}))
return (retoptions, filenames, excluded)
# ___ __ _ ___ _
# / __|___ _ _ / _(_)__ _ | _ \___ __ _ __| |___ _ _
# | (__/ _ \ ' \| _| / _` | | / -_) _` / _` / -_) '_|
@ -587,6 +486,20 @@ def print_report(results, cmdline, unlintable_filenames, cant_lint_filenames,
print("\n".join([" {}".format(f) for f in unfindable_filenames]))
def print_help(options_list, name):
print(_('Usage: {} [options] [filenames]').format(name))
for item in options_list:
print(' -{:<1} --{:<12} {}'.format(item[0], item[1], item[3]))
return sys.exit()
def print_version(name, version):
print('{} {} Copyright (c) 2009, 2016 Kennth M. "Elf" Sternberg'.format(name, version))
def run_gitlint(cmdline, config, extras):
def build_config_subset(keys):
@ -630,16 +543,3 @@ def run_gitlint(cmdline, config, extras):
if not len(results):
return 0
return max([i[2] for i in results if len(i)])
def print_help(options_list, name):
print(_('Usage: {} [options] [filenames]').format(name))
for item in options_list:
print(' -{:<1} --{:<12} {}'.format(item[0], item[1], item[3]))
return sys.exit()
def print_version(name, version):
print('{} {} Copyright (c) 2009, 2016 Kennth M. "Elf" Sternberg'.format(name, version))

View File

@ -0,0 +1,77 @@
#!/usr/bin/env python
#
# Copyright (C) 2015 Elf M. Sternberg
# Author: Elf M. Sternberg
#
# This was a lot shorter and smarter in Hy...
def make_rational_options(options, commandline):
"""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.
: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
"""
def make_options_rationalizer(options):
"""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 = {}
for option in options:
if not option[1]:
continue
if option[0]:
fullset['-' + option[0]] = option[1]
fullset['--' + option[1]] = option[1]
def rationalizer(acc, it):
acc[fullset[it[0]]] = it[1]
return acc
return rationalizer
def remove_conflicted_options(options, request):
"""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 [])
keys = request.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[0] + ((i[2] and ':') or '')
def longoptstogo(i): return i[1] + ((i[2] and '=') or '')
optstringsshort = ''.join([shortoptstogo(opt) for opt in options])
optstringslong = [longoptstogo(opt) for opt in options]
(options, filenames) = getopt.getopt(commandline[1:],
optstringsshort,
optstringslong)
# Turns what getopt returns into something more human-readable
rationalize_options = make_options_rationalizer(options)
# Remove any options that are superseded by others.
(retoptions, excluded) = remove_conflicted_options(
optlist, reduce(rationalize_options, options, {}))
return (retoptions, filenames, excluded)

35
git_lint/options.py Normal file
View File

@ -0,0 +1,35 @@
OPTIONS_LIST = [
('o', 'only', True,
_('A comma-separated list of only those linters to run'), ['exclude']),
('x', 'exclude', True,
_('A comma-separated list of linters to skip'), []),
('l', 'linters', False,
_('Show the list of configured linters'), []),
('b', 'base', False,
_('Check all changed files in the repository, not just those in the current directory.'), []),
('a', 'all', False,
_('Scan all files in the repository, not just those that have changed.'), []),
('e', 'every', False,
_('Short for -b -a: scan everything'), []),
('w', 'workspace', False,
_('Scan the workspace'), ['staging']),
('s', 'staging', False,
_('Scan the staging area (useful for pre-commit).'), []),
('g', 'changes', False,
_("Report lint failures only for diff'd sections"), ['complete']),
('p', 'complete', False,
_('Report lint failures for all files'), []),
('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', 'version', False,
_('Version information'), [])
]