Add pre-commit try-repo

`try-repo` is useful for:
- Trying out a remote hook repository without needing to configure it.
- Testing a hook repository while developing it.
This commit is contained in:
Anthony Sottile
2017-10-07 15:13:53 -07:00
parent e8641ee0a3
commit 2c88791a7f
15 changed files with 254 additions and 110 deletions

View File

@@ -0,0 +1,44 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import collections
import os.path
from aspy.yaml import ordered_dump
import pre_commit.constants as C
from pre_commit import git
from pre_commit import output
from pre_commit.commands.run import run
from pre_commit.manifest import Manifest
from pre_commit.runner import Runner
from pre_commit.store import Store
from pre_commit.util import tmpdir
def try_repo(args):
ref = args.ref or git.head_sha(args.repo)
with tmpdir() as tempdir:
if args.hook:
hooks = [{'id': args.hook}]
else:
manifest = Manifest(Store(tempdir).clone(args.repo, ref))
hooks = [{'id': hook_id} for hook_id in sorted(manifest.hooks)]
items = (('repo', args.repo), ('sha', ref), ('hooks', hooks))
config = {'repos': [collections.OrderedDict(items)]}
config_s = ordered_dump(config, **C.YAML_DUMP_KWARGS)
config_filename = os.path.join(tempdir, C.CONFIG_FILE)
with open(config_filename, 'w') as cfg:
cfg.write(config_s)
output.write_line('=' * 79)
output.write_line('Using config:')
output.write_line('=' * 79)
output.write(config_s)
output.write_line('=' * 79)
runner = Runner('.', config_filename, store_dir=tempdir)
return run(runner, args)

View File

@@ -97,6 +97,11 @@ def get_changed_files(new, old):
)[1])
def head_sha(remote):
_, out, _ = cmd_output('git', 'ls-remote', '--exit-code', remote, 'HEAD')
return out.split()[0]
def check_for_cygwin_mismatch():
"""See https://github.com/pre-commit/pre-commit/issues/354"""
if sys.platform in ('cygwin', 'win32'): # pragma: no cover (windows)

View File

@@ -17,6 +17,7 @@ from pre_commit.commands.install_uninstall import uninstall
from pre_commit.commands.migrate_config import migrate_config
from pre_commit.commands.run import run
from pre_commit.commands.sample_config import sample_config
from pre_commit.commands.try_repo import try_repo
from pre_commit.error_handler import error_handler
from pre_commit.logging_handler import add_logging_handler
from pre_commit.runner import Runner
@@ -53,6 +54,41 @@ def _add_hook_type_option(parser):
)
def _add_run_options(parser):
parser.add_argument('hook', nargs='?', help='A single hook-id to run')
parser.add_argument('--verbose', '-v', action='store_true', default=False)
parser.add_argument(
'--origin', '-o',
help="The origin branch's commit_id when using `git push`.",
)
parser.add_argument(
'--source', '-s',
help="The remote branch's commit_id when using `git push`.",
)
parser.add_argument(
'--commit-msg-filename',
help='Filename to check when running during `commit-msg`',
)
parser.add_argument(
'--hook-stage', choices=('commit', 'push', 'commit-msg'),
default='commit',
help='The stage during which the hook is fired e.g. commit or push.',
)
parser.add_argument(
'--show-diff-on-failure', action='store_true',
help='When hooks fail, run `git diff` directly afterward.',
)
mutex_group = parser.add_mutually_exclusive_group(required=False)
mutex_group.add_argument(
'--all-files', '-a', action='store_true', default=False,
help='Run on all the files in the repo.',
)
mutex_group.add_argument(
'--files', nargs='*', default=[],
help='Specific filenames to run hooks on.',
)
def main(argv=None):
argv = argv if argv is not None else sys.argv[1:]
argv = [five.to_text(arg) for arg in argv]
@@ -142,40 +178,7 @@ def main(argv=None):
run_parser = subparsers.add_parser('run', help='Run hooks.')
_add_color_option(run_parser)
_add_config_option(run_parser)
run_parser.add_argument('hook', nargs='?', help='A single hook-id to run')
run_parser.add_argument(
'--verbose', '-v', action='store_true', default=False,
)
run_parser.add_argument(
'--origin', '-o',
help="The origin branch's commit_id when using `git push`.",
)
run_parser.add_argument(
'--source', '-s',
help="The remote branch's commit_id when using `git push`.",
)
run_parser.add_argument(
'--commit-msg-filename',
help='Filename to check when running during `commit-msg`',
)
run_parser.add_argument(
'--hook-stage', choices=('commit', 'push', 'commit-msg'),
default='commit',
help='The stage during which the hook is fired e.g. commit or push.',
)
run_parser.add_argument(
'--show-diff-on-failure', action='store_true',
help='When hooks fail, run `git diff` directly afterward.',
)
run_mutex_group = run_parser.add_mutually_exclusive_group(required=False)
run_mutex_group.add_argument(
'--all-files', '-a', action='store_true', default=False,
help='Run on all the files in the repo.',
)
run_mutex_group.add_argument(
'--files', nargs='*', default=[],
help='Specific filenames to run hooks on.',
)
_add_run_options(run_parser)
sample_config_parser = subparsers.add_parser(
'sample-config', help='Produce a sample {} file'.format(C.CONFIG_FILE),
@@ -183,6 +186,24 @@ def main(argv=None):
_add_color_option(sample_config_parser)
_add_config_option(sample_config_parser)
try_repo_parser = subparsers.add_parser(
'try-repo',
help='Try the hooks in a repository, useful for developing new hooks.',
)
_add_color_option(try_repo_parser)
_add_config_option(try_repo_parser)
try_repo_parser.add_argument(
'repo', help='Repository to source hooks from.',
)
try_repo_parser.add_argument(
'--ref',
help=(
'Manually select a ref to run against, otherwise the `HEAD` '
'revision will be used.'
),
)
_add_run_options(try_repo_parser)
help = subparsers.add_parser(
'help', help='Show help for a specific command.',
)
@@ -231,6 +252,8 @@ def main(argv=None):
return run(runner, args)
elif args.command == 'sample-config':
return sample_config()
elif args.command == 'try-repo':
return try_repo(args)
else:
raise NotImplementedError(
'Command {} not implemented.'.format(args.command),

View File

@@ -14,9 +14,8 @@ logger = logging.getLogger('pre_commit')
class Manifest(object):
def __init__(self, repo_path, repo_url):
def __init__(self, repo_path):
self.repo_path = repo_path
self.repo_url = repo_url
@cached_property
def manifest_contents(self):

View File

@@ -146,7 +146,7 @@ class Repository(object):
@cached_property
def manifest(self):
return Manifest(self._repo_path, self.repo_config['repo'])
return Manifest(self._repo_path)
@cached_property
def hooks(self):

View File

@@ -15,13 +15,14 @@ class Runner(object):
repository under test.
"""
def __init__(self, git_root, config_file):
def __init__(self, git_root, config_file, store_dir=None):
self.git_root = git_root
self.config_file = config_file
self._store_dir = store_dir
@classmethod
def create(cls, config_file):
"""Creates a PreCommitRunner by doing the following:
"""Creates a Runner by doing the following:
- Finds the root of the current git repository
- chdir to that directory
"""
@@ -63,4 +64,4 @@ class Runner(object):
@cached_property
def store(self):
return Store()
return Store(self._store_dir)