mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-04-14 17:41:45 +04:00
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:
parent
e8641ee0a3
commit
2c88791a7f
15 changed files with 254 additions and 110 deletions
44
pre_commit/commands/try_repo.py
Normal file
44
pre_commit/commands/try_repo.py
Normal 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)
|
||||||
|
|
@ -97,6 +97,11 @@ def get_changed_files(new, old):
|
||||||
)[1])
|
)[1])
|
||||||
|
|
||||||
|
|
||||||
|
def head_sha(remote):
|
||||||
|
_, out, _ = cmd_output('git', 'ls-remote', '--exit-code', remote, 'HEAD')
|
||||||
|
return out.split()[0]
|
||||||
|
|
||||||
|
|
||||||
def check_for_cygwin_mismatch():
|
def check_for_cygwin_mismatch():
|
||||||
"""See https://github.com/pre-commit/pre-commit/issues/354"""
|
"""See https://github.com/pre-commit/pre-commit/issues/354"""
|
||||||
if sys.platform in ('cygwin', 'win32'): # pragma: no cover (windows)
|
if sys.platform in ('cygwin', 'win32'): # pragma: no cover (windows)
|
||||||
|
|
|
||||||
|
|
@ -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.migrate_config import migrate_config
|
||||||
from pre_commit.commands.run import run
|
from pre_commit.commands.run import run
|
||||||
from pre_commit.commands.sample_config import sample_config
|
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.error_handler import error_handler
|
||||||
from pre_commit.logging_handler import add_logging_handler
|
from pre_commit.logging_handler import add_logging_handler
|
||||||
from pre_commit.runner import Runner
|
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):
|
def main(argv=None):
|
||||||
argv = argv if argv is not None else sys.argv[1:]
|
argv = argv if argv is not None else sys.argv[1:]
|
||||||
argv = [five.to_text(arg) for arg in argv]
|
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.')
|
run_parser = subparsers.add_parser('run', help='Run hooks.')
|
||||||
_add_color_option(run_parser)
|
_add_color_option(run_parser)
|
||||||
_add_config_option(run_parser)
|
_add_config_option(run_parser)
|
||||||
run_parser.add_argument('hook', nargs='?', help='A single hook-id to run')
|
_add_run_options(run_parser)
|
||||||
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.',
|
|
||||||
)
|
|
||||||
|
|
||||||
sample_config_parser = subparsers.add_parser(
|
sample_config_parser = subparsers.add_parser(
|
||||||
'sample-config', help='Produce a sample {} file'.format(C.CONFIG_FILE),
|
'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_color_option(sample_config_parser)
|
||||||
_add_config_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 = subparsers.add_parser(
|
||||||
'help', help='Show help for a specific command.',
|
'help', help='Show help for a specific command.',
|
||||||
)
|
)
|
||||||
|
|
@ -231,6 +252,8 @@ def main(argv=None):
|
||||||
return run(runner, args)
|
return run(runner, args)
|
||||||
elif args.command == 'sample-config':
|
elif args.command == 'sample-config':
|
||||||
return sample_config()
|
return sample_config()
|
||||||
|
elif args.command == 'try-repo':
|
||||||
|
return try_repo(args)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'Command {} not implemented.'.format(args.command),
|
'Command {} not implemented.'.format(args.command),
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,8 @@ logger = logging.getLogger('pre_commit')
|
||||||
|
|
||||||
|
|
||||||
class Manifest(object):
|
class Manifest(object):
|
||||||
def __init__(self, repo_path, repo_url):
|
def __init__(self, repo_path):
|
||||||
self.repo_path = repo_path
|
self.repo_path = repo_path
|
||||||
self.repo_url = repo_url
|
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def manifest_contents(self):
|
def manifest_contents(self):
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ class Repository(object):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def manifest(self):
|
def manifest(self):
|
||||||
return Manifest(self._repo_path, self.repo_config['repo'])
|
return Manifest(self._repo_path)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def hooks(self):
|
def hooks(self):
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,14 @@ class Runner(object):
|
||||||
repository under test.
|
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.git_root = git_root
|
||||||
self.config_file = config_file
|
self.config_file = config_file
|
||||||
|
self._store_dir = store_dir
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, config_file):
|
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
|
- Finds the root of the current git repository
|
||||||
- chdir to that directory
|
- chdir to that directory
|
||||||
"""
|
"""
|
||||||
|
|
@ -63,4 +64,4 @@ class Runner(object):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def store(self):
|
def store(self):
|
||||||
return Store()
|
return Store(self._store_dir)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ from aspy.yaml import ordered_dump
|
||||||
from aspy.yaml import ordered_load
|
from aspy.yaml import ordered_load
|
||||||
|
|
||||||
import pre_commit.constants as C
|
import pre_commit.constants as C
|
||||||
|
from pre_commit import git
|
||||||
from pre_commit.clientlib import CONFIG_SCHEMA
|
from pre_commit.clientlib import CONFIG_SCHEMA
|
||||||
from pre_commit.clientlib import load_manifest
|
from pre_commit.clientlib import load_manifest
|
||||||
from pre_commit.schema import apply_defaults
|
from pre_commit.schema import apply_defaults
|
||||||
|
|
@ -17,7 +18,6 @@ from pre_commit.schema import validate
|
||||||
from pre_commit.util import cmd_output
|
from pre_commit.util import cmd_output
|
||||||
from pre_commit.util import copy_tree_to_path
|
from pre_commit.util import copy_tree_to_path
|
||||||
from pre_commit.util import cwd
|
from pre_commit.util import cwd
|
||||||
from testing.util import get_head_sha
|
|
||||||
from testing.util import get_resource_path
|
from testing.util import get_resource_path
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ def make_config_from_repo(repo_path, sha=None, hooks=None, check=True):
|
||||||
manifest = load_manifest(os.path.join(repo_path, C.MANIFEST_FILE))
|
manifest = load_manifest(os.path.join(repo_path, C.MANIFEST_FILE))
|
||||||
config = OrderedDict((
|
config = OrderedDict((
|
||||||
('repo', 'file://{}'.format(repo_path)),
|
('repo', 'file://{}'.format(repo_path)),
|
||||||
('sha', sha or get_head_sha(repo_path)),
|
('sha', sha or git.head_sha(repo_path)),
|
||||||
(
|
(
|
||||||
'hooks',
|
'hooks',
|
||||||
hooks or [OrderedDict((('id', hook['id']),)) for hook in manifest],
|
hooks or [OrderedDict((('id', hook['id']),)) for hook in manifest],
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from pre_commit import parse_shebang
|
||||||
from pre_commit.languages.docker import docker_is_running
|
from pre_commit.languages.docker import docker_is_running
|
||||||
from pre_commit.languages.pcre import GREP
|
from pre_commit.languages.pcre import GREP
|
||||||
from pre_commit.util import cmd_output
|
from pre_commit.util import cmd_output
|
||||||
from pre_commit.util import cwd
|
from testing.auto_namedtuple import auto_namedtuple
|
||||||
|
|
||||||
|
|
||||||
TESTING_DIR = os.path.abspath(os.path.dirname(__file__))
|
TESTING_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
@ -18,11 +18,6 @@ def get_resource_path(path):
|
||||||
return os.path.join(TESTING_DIR, 'resources', path)
|
return os.path.join(TESTING_DIR, 'resources', path)
|
||||||
|
|
||||||
|
|
||||||
def get_head_sha(dir):
|
|
||||||
with cwd(dir):
|
|
||||||
return cmd_output('git', 'rev-parse', 'HEAD')[1].strip()
|
|
||||||
|
|
||||||
|
|
||||||
def cmd_output_mocked_pre_commit_home(*args, **kwargs):
|
def cmd_output_mocked_pre_commit_home(*args, **kwargs):
|
||||||
# keyword-only argument
|
# keyword-only argument
|
||||||
tempdir_factory = kwargs.pop('tempdir_factory')
|
tempdir_factory = kwargs.pop('tempdir_factory')
|
||||||
|
|
@ -72,3 +67,31 @@ xfailif_no_symlink = pytest.mark.xfail(
|
||||||
not hasattr(os, 'symlink'),
|
not hasattr(os, 'symlink'),
|
||||||
reason='Symlink is not supported on this platform',
|
reason='Symlink is not supported on this platform',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_opts(
|
||||||
|
all_files=False,
|
||||||
|
files=(),
|
||||||
|
color=False,
|
||||||
|
verbose=False,
|
||||||
|
hook=None,
|
||||||
|
origin='',
|
||||||
|
source='',
|
||||||
|
hook_stage='commit',
|
||||||
|
show_diff_on_failure=False,
|
||||||
|
commit_msg_filename='',
|
||||||
|
):
|
||||||
|
# These are mutually exclusive
|
||||||
|
assert not (all_files and files)
|
||||||
|
return auto_namedtuple(
|
||||||
|
all_files=all_files,
|
||||||
|
files=files,
|
||||||
|
color=color,
|
||||||
|
verbose=verbose,
|
||||||
|
hook=hook,
|
||||||
|
origin=origin,
|
||||||
|
source=source,
|
||||||
|
hook_stage=hook_stage,
|
||||||
|
show_diff_on_failure=show_diff_on_failure,
|
||||||
|
commit_msg_filename=commit_msg_filename,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ from collections import OrderedDict
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import pre_commit.constants as C
|
import pre_commit.constants as C
|
||||||
|
from pre_commit import git
|
||||||
from pre_commit.clientlib import load_config
|
from pre_commit.clientlib import load_config
|
||||||
from pre_commit.commands.autoupdate import _update_repo
|
from pre_commit.commands.autoupdate import _update_repo
|
||||||
from pre_commit.commands.autoupdate import autoupdate
|
from pre_commit.commands.autoupdate import autoupdate
|
||||||
|
|
@ -21,7 +22,6 @@ from testing.fixtures import git_dir
|
||||||
from testing.fixtures import make_config_from_repo
|
from testing.fixtures import make_config_from_repo
|
||||||
from testing.fixtures import make_repo
|
from testing.fixtures import make_repo
|
||||||
from testing.fixtures import write_config
|
from testing.fixtures import write_config
|
||||||
from testing.util import get_head_sha
|
|
||||||
from testing.util import get_resource_path
|
from testing.util import get_resource_path
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -66,10 +66,10 @@ def test_autoupdate_old_revision_broken(
|
||||||
cmd_output('git', 'mv', C.MANIFEST_FILE, 'nope.yaml')
|
cmd_output('git', 'mv', C.MANIFEST_FILE, 'nope.yaml')
|
||||||
cmd_output('git', 'commit', '-m', 'simulate old repo')
|
cmd_output('git', 'commit', '-m', 'simulate old repo')
|
||||||
# Assume this is the revision the user's old repository was at
|
# Assume this is the revision the user's old repository was at
|
||||||
rev = get_head_sha(path)
|
rev = git.head_sha(path)
|
||||||
cmd_output('git', 'mv', 'nope.yaml', C.MANIFEST_FILE)
|
cmd_output('git', 'mv', 'nope.yaml', C.MANIFEST_FILE)
|
||||||
cmd_output('git', 'commit', '-m', 'move hooks file')
|
cmd_output('git', 'commit', '-m', 'move hooks file')
|
||||||
update_rev = get_head_sha(path)
|
update_rev = git.head_sha(path)
|
||||||
|
|
||||||
config['sha'] = rev
|
config['sha'] = rev
|
||||||
write_config('.', config)
|
write_config('.', config)
|
||||||
|
|
@ -84,12 +84,12 @@ def test_autoupdate_old_revision_broken(
|
||||||
@pytest.yield_fixture
|
@pytest.yield_fixture
|
||||||
def out_of_date_repo(tempdir_factory):
|
def out_of_date_repo(tempdir_factory):
|
||||||
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
||||||
original_sha = get_head_sha(path)
|
original_sha = git.head_sha(path)
|
||||||
|
|
||||||
# Make a commit
|
# Make a commit
|
||||||
with cwd(path):
|
with cwd(path):
|
||||||
cmd_output('git', 'commit', '--allow-empty', '-m', 'foo')
|
cmd_output('git', 'commit', '--allow-empty', '-m', 'foo')
|
||||||
head_sha = get_head_sha(path)
|
head_sha = git.head_sha(path)
|
||||||
|
|
||||||
yield auto_namedtuple(
|
yield auto_namedtuple(
|
||||||
path=path, original_sha=original_sha, head_sha=head_sha,
|
path=path, original_sha=original_sha, head_sha=head_sha,
|
||||||
|
|
@ -225,7 +225,7 @@ def test_autoupdate_tags_only(
|
||||||
@pytest.yield_fixture
|
@pytest.yield_fixture
|
||||||
def hook_disappearing_repo(tempdir_factory):
|
def hook_disappearing_repo(tempdir_factory):
|
||||||
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
||||||
original_sha = get_head_sha(path)
|
original_sha = git.head_sha(path)
|
||||||
|
|
||||||
with cwd(path):
|
with cwd(path):
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@ from pre_commit.runner import Runner
|
||||||
from pre_commit.util import cmd_output
|
from pre_commit.util import cmd_output
|
||||||
from pre_commit.util import cwd
|
from pre_commit.util import cwd
|
||||||
from pre_commit.util import make_executable
|
from pre_commit.util import make_executable
|
||||||
from testing.auto_namedtuple import auto_namedtuple
|
|
||||||
from testing.fixtures import add_config_to_repo
|
from testing.fixtures import add_config_to_repo
|
||||||
from testing.fixtures import make_consuming_repo
|
from testing.fixtures import make_consuming_repo
|
||||||
from testing.fixtures import modify_config
|
from testing.fixtures import modify_config
|
||||||
from testing.fixtures import read_config
|
from testing.fixtures import read_config
|
||||||
from testing.util import cmd_output_mocked_pre_commit_home
|
from testing.util import cmd_output_mocked_pre_commit_home
|
||||||
|
from testing.util import run_opts
|
||||||
from testing.util import xfailif_no_symlink
|
from testing.util import xfailif_no_symlink
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,34 +48,6 @@ def stage_a_file(filename='foo.py'):
|
||||||
cmd_output('git', 'add', filename)
|
cmd_output('git', 'add', filename)
|
||||||
|
|
||||||
|
|
||||||
def _get_opts(
|
|
||||||
all_files=False,
|
|
||||||
files=(),
|
|
||||||
color=False,
|
|
||||||
verbose=False,
|
|
||||||
hook=None,
|
|
||||||
origin='',
|
|
||||||
source='',
|
|
||||||
hook_stage='commit',
|
|
||||||
show_diff_on_failure=False,
|
|
||||||
commit_msg_filename='',
|
|
||||||
):
|
|
||||||
# These are mutually exclusive
|
|
||||||
assert not (all_files and files)
|
|
||||||
return auto_namedtuple(
|
|
||||||
all_files=all_files,
|
|
||||||
files=files,
|
|
||||||
color=color,
|
|
||||||
verbose=verbose,
|
|
||||||
hook=hook,
|
|
||||||
origin=origin,
|
|
||||||
source=source,
|
|
||||||
hook_stage=hook_stage,
|
|
||||||
show_diff_on_failure=show_diff_on_failure,
|
|
||||||
commit_msg_filename=commit_msg_filename,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _do_run(cap_out, repo, args, environ={}, config_file=C.CONFIG_FILE):
|
def _do_run(cap_out, repo, args, environ={}, config_file=C.CONFIG_FILE):
|
||||||
runner = Runner(repo, config_file)
|
runner = Runner(repo, config_file)
|
||||||
with cwd(runner.git_root): # replicates Runner.create behaviour
|
with cwd(runner.git_root): # replicates Runner.create behaviour
|
||||||
|
|
@ -90,7 +62,7 @@ def _test_run(
|
||||||
):
|
):
|
||||||
if stage:
|
if stage:
|
||||||
stage_a_file()
|
stage_a_file()
|
||||||
args = _get_opts(**opts)
|
args = run_opts(**opts)
|
||||||
ret, printed = _do_run(cap_out, repo, args, config_file=config_file)
|
ret, printed = _do_run(cap_out, repo, args, config_file=config_file)
|
||||||
|
|
||||||
assert ret == expected_ret, (ret, expected_ret, printed)
|
assert ret == expected_ret, (ret, expected_ret, printed)
|
||||||
|
|
@ -161,7 +133,7 @@ def test_types_hook_repository(
|
||||||
with cwd(git_path):
|
with cwd(git_path):
|
||||||
stage_a_file('bar.py')
|
stage_a_file('bar.py')
|
||||||
stage_a_file('bar.notpy')
|
stage_a_file('bar.notpy')
|
||||||
ret, printed = _do_run(cap_out, git_path, _get_opts())
|
ret, printed = _do_run(cap_out, git_path, run_opts())
|
||||||
assert ret == 1
|
assert ret == 1
|
||||||
assert b'bar.py' in printed
|
assert b'bar.py' in printed
|
||||||
assert b'bar.notpy' not in printed
|
assert b'bar.notpy' not in printed
|
||||||
|
|
@ -177,7 +149,7 @@ def test_exclude_types_hook_repository(
|
||||||
make_executable('exe')
|
make_executable('exe')
|
||||||
cmd_output('git', 'add', 'exe')
|
cmd_output('git', 'add', 'exe')
|
||||||
stage_a_file('bar.py')
|
stage_a_file('bar.py')
|
||||||
ret, printed = _do_run(cap_out, git_path, _get_opts())
|
ret, printed = _do_run(cap_out, git_path, run_opts())
|
||||||
assert ret == 1
|
assert ret == 1
|
||||||
assert b'bar.py' in printed
|
assert b'bar.py' in printed
|
||||||
assert b'exe' not in printed
|
assert b'exe' not in printed
|
||||||
|
|
@ -191,7 +163,7 @@ def test_global_exclude(cap_out, tempdir_factory, mock_out_store_directory):
|
||||||
open('foo.py', 'a').close()
|
open('foo.py', 'a').close()
|
||||||
open('bar.py', 'a').close()
|
open('bar.py', 'a').close()
|
||||||
cmd_output('git', 'add', '.')
|
cmd_output('git', 'add', '.')
|
||||||
ret, printed = _do_run(cap_out, git_path, _get_opts(verbose=True))
|
ret, printed = _do_run(cap_out, git_path, run_opts(verbose=True))
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
# Does not contain foo.py since it was excluded
|
# Does not contain foo.py since it was excluded
|
||||||
expected = b'hookid: bash_hook\n\nbar.py\nHello World\n\n'
|
expected = b'hookid: bash_hook\n\nbar.py\nHello World\n\n'
|
||||||
|
|
@ -332,7 +304,7 @@ def test_origin_source_error_msg(
|
||||||
repo_with_passing_hook, origin, source, expect_failure,
|
repo_with_passing_hook, origin, source, expect_failure,
|
||||||
mock_out_store_directory, cap_out,
|
mock_out_store_directory, cap_out,
|
||||||
):
|
):
|
||||||
args = _get_opts(origin=origin, source=source)
|
args = run_opts(origin=origin, source=source)
|
||||||
ret, printed = _do_run(cap_out, repo_with_passing_hook, args)
|
ret, printed = _do_run(cap_out, repo_with_passing_hook, args)
|
||||||
warning_msg = b'Specify both --origin and --source.'
|
warning_msg = b'Specify both --origin and --source.'
|
||||||
if expect_failure:
|
if expect_failure:
|
||||||
|
|
@ -350,7 +322,7 @@ def test_has_unmerged_paths(in_merge_conflict):
|
||||||
|
|
||||||
|
|
||||||
def test_merge_conflict(cap_out, in_merge_conflict, mock_out_store_directory):
|
def test_merge_conflict(cap_out, in_merge_conflict, mock_out_store_directory):
|
||||||
ret, printed = _do_run(cap_out, in_merge_conflict, _get_opts())
|
ret, printed = _do_run(cap_out, in_merge_conflict, run_opts())
|
||||||
assert ret == 1
|
assert ret == 1
|
||||||
assert b'Unmerged files. Resolve before committing.' in printed
|
assert b'Unmerged files. Resolve before committing.' in printed
|
||||||
|
|
||||||
|
|
@ -363,7 +335,7 @@ def test_merge_conflict_modified(
|
||||||
with open('dummy', 'w') as dummy_file:
|
with open('dummy', 'w') as dummy_file:
|
||||||
dummy_file.write('bar\nbaz\n')
|
dummy_file.write('bar\nbaz\n')
|
||||||
|
|
||||||
ret, printed = _do_run(cap_out, in_merge_conflict, _get_opts())
|
ret, printed = _do_run(cap_out, in_merge_conflict, run_opts())
|
||||||
assert ret == 1
|
assert ret == 1
|
||||||
assert b'Unmerged files. Resolve before committing.' in printed
|
assert b'Unmerged files. Resolve before committing.' in printed
|
||||||
|
|
||||||
|
|
@ -372,7 +344,7 @@ def test_merge_conflict_resolved(
|
||||||
cap_out, in_merge_conflict, mock_out_store_directory,
|
cap_out, in_merge_conflict, mock_out_store_directory,
|
||||||
):
|
):
|
||||||
cmd_output('git', 'add', '.')
|
cmd_output('git', 'add', '.')
|
||||||
ret, printed = _do_run(cap_out, in_merge_conflict, _get_opts())
|
ret, printed = _do_run(cap_out, in_merge_conflict, run_opts())
|
||||||
for msg in (
|
for msg in (
|
||||||
b'Checking merge-conflict files only.', b'Bash hook', b'Passed',
|
b'Checking merge-conflict files only.', b'Bash hook', b'Passed',
|
||||||
):
|
):
|
||||||
|
|
@ -415,7 +387,7 @@ def test_get_skips(environ, expected_output):
|
||||||
|
|
||||||
def test_skip_hook(cap_out, repo_with_passing_hook, mock_out_store_directory):
|
def test_skip_hook(cap_out, repo_with_passing_hook, mock_out_store_directory):
|
||||||
ret, printed = _do_run(
|
ret, printed = _do_run(
|
||||||
cap_out, repo_with_passing_hook, _get_opts(), {'SKIP': 'bash_hook'},
|
cap_out, repo_with_passing_hook, run_opts(), {'SKIP': 'bash_hook'},
|
||||||
)
|
)
|
||||||
for msg in (b'Bash hook', b'Skipped'):
|
for msg in (b'Bash hook', b'Skipped'):
|
||||||
assert msg in printed
|
assert msg in printed
|
||||||
|
|
@ -425,7 +397,7 @@ def test_hook_id_not_in_non_verbose_output(
|
||||||
cap_out, repo_with_passing_hook, mock_out_store_directory,
|
cap_out, repo_with_passing_hook, mock_out_store_directory,
|
||||||
):
|
):
|
||||||
ret, printed = _do_run(
|
ret, printed = _do_run(
|
||||||
cap_out, repo_with_passing_hook, _get_opts(verbose=False),
|
cap_out, repo_with_passing_hook, run_opts(verbose=False),
|
||||||
)
|
)
|
||||||
assert b'[bash_hook]' not in printed
|
assert b'[bash_hook]' not in printed
|
||||||
|
|
||||||
|
|
@ -434,7 +406,7 @@ def test_hook_id_in_verbose_output(
|
||||||
cap_out, repo_with_passing_hook, mock_out_store_directory,
|
cap_out, repo_with_passing_hook, mock_out_store_directory,
|
||||||
):
|
):
|
||||||
ret, printed = _do_run(
|
ret, printed = _do_run(
|
||||||
cap_out, repo_with_passing_hook, _get_opts(verbose=True),
|
cap_out, repo_with_passing_hook, run_opts(verbose=True),
|
||||||
)
|
)
|
||||||
assert b'[bash_hook] Bash hook' in printed
|
assert b'[bash_hook] Bash hook' in printed
|
||||||
|
|
||||||
|
|
@ -448,7 +420,7 @@ def test_multiple_hooks_same_id(
|
||||||
config['repos'][0]['hooks'].append({'id': 'bash_hook'})
|
config['repos'][0]['hooks'].append({'id': 'bash_hook'})
|
||||||
stage_a_file()
|
stage_a_file()
|
||||||
|
|
||||||
ret, output = _do_run(cap_out, repo_with_passing_hook, _get_opts())
|
ret, output = _do_run(cap_out, repo_with_passing_hook, run_opts())
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
assert output.count(b'Bash hook') == 2
|
assert output.count(b'Bash hook') == 2
|
||||||
|
|
||||||
|
|
@ -684,7 +656,7 @@ def modified_config_repo(repo_with_passing_hook):
|
||||||
def test_error_with_unstaged_config(
|
def test_error_with_unstaged_config(
|
||||||
cap_out, modified_config_repo, mock_out_store_directory,
|
cap_out, modified_config_repo, mock_out_store_directory,
|
||||||
):
|
):
|
||||||
args = _get_opts()
|
args = run_opts()
|
||||||
ret, printed = _do_run(cap_out, modified_config_repo, args)
|
ret, printed = _do_run(cap_out, modified_config_repo, args)
|
||||||
assert b'Your .pre-commit-config.yaml is unstaged.' in printed
|
assert b'Your .pre-commit-config.yaml is unstaged.' in printed
|
||||||
assert ret == 1
|
assert ret == 1
|
||||||
|
|
@ -696,7 +668,7 @@ def test_error_with_unstaged_config(
|
||||||
def test_no_unstaged_error_with_all_files_or_files(
|
def test_no_unstaged_error_with_all_files_or_files(
|
||||||
cap_out, modified_config_repo, mock_out_store_directory, opts,
|
cap_out, modified_config_repo, mock_out_store_directory, opts,
|
||||||
):
|
):
|
||||||
args = _get_opts(**opts)
|
args = run_opts(**opts)
|
||||||
ret, printed = _do_run(cap_out, modified_config_repo, args)
|
ret, printed = _do_run(cap_out, modified_config_repo, args)
|
||||||
assert b'Your .pre-commit-config.yaml is unstaged.' not in printed
|
assert b'Your .pre-commit-config.yaml is unstaged.' not in printed
|
||||||
|
|
||||||
|
|
@ -742,7 +714,7 @@ def test_pass_filenames(
|
||||||
config['repos'][0]['hooks'][0]['args'] = hook_args
|
config['repos'][0]['hooks'][0]['args'] = hook_args
|
||||||
stage_a_file()
|
stage_a_file()
|
||||||
ret, printed = _do_run(
|
ret, printed = _do_run(
|
||||||
cap_out, repo_with_passing_hook, _get_opts(verbose=True),
|
cap_out, repo_with_passing_hook, run_opts(verbose=True),
|
||||||
)
|
)
|
||||||
assert expected_out + b'\nHello World' in printed
|
assert expected_out + b'\nHello World' in printed
|
||||||
assert (b'foo.py' in printed) == pass_filenames
|
assert (b'foo.py' in printed) == pass_filenames
|
||||||
|
|
@ -758,7 +730,7 @@ def test_fail_fast(
|
||||||
config['repos'][0]['hooks'] *= 2
|
config['repos'][0]['hooks'] *= 2
|
||||||
stage_a_file()
|
stage_a_file()
|
||||||
|
|
||||||
ret, printed = _do_run(cap_out, repo_with_failing_hook, _get_opts())
|
ret, printed = _do_run(cap_out, repo_with_failing_hook, run_opts())
|
||||||
# it should have only run one hook
|
# it should have only run one hook
|
||||||
assert printed.count(b'Failing hook') == 1
|
assert printed.count(b'Failing hook') == 1
|
||||||
|
|
||||||
|
|
|
||||||
71
tests/commands/try_repo_test.py
Normal file
71
tests/commands/try_repo_test.py
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from pre_commit.commands.try_repo import try_repo
|
||||||
|
from pre_commit.util import cmd_output
|
||||||
|
from pre_commit.util import cwd
|
||||||
|
from testing.auto_namedtuple import auto_namedtuple
|
||||||
|
from testing.fixtures import git_dir
|
||||||
|
from testing.fixtures import make_repo
|
||||||
|
from testing.util import run_opts
|
||||||
|
|
||||||
|
|
||||||
|
def try_repo_opts(repo, ref=None, **kwargs):
|
||||||
|
return auto_namedtuple(repo=repo, ref=ref, **run_opts(**kwargs)._asdict())
|
||||||
|
|
||||||
|
|
||||||
|
def _get_out(cap_out):
|
||||||
|
out = cap_out.get().replace('\r\n', '\n')
|
||||||
|
out = re.sub('\[INFO\].+\n', '', out)
|
||||||
|
start, using_config, config, rest = out.split('=' * 79 + '\n')
|
||||||
|
assert start == ''
|
||||||
|
assert using_config == 'Using config:\n'
|
||||||
|
return config, rest
|
||||||
|
|
||||||
|
|
||||||
|
def _run_try_repo(tempdir_factory, **kwargs):
|
||||||
|
repo = make_repo(tempdir_factory, 'modified_file_returns_zero_repo')
|
||||||
|
with cwd(git_dir(tempdir_factory)):
|
||||||
|
open('test-file', 'a').close()
|
||||||
|
cmd_output('git', 'add', '.')
|
||||||
|
assert not try_repo(try_repo_opts(repo, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
def test_try_repo_repo_only(cap_out, tempdir_factory):
|
||||||
|
_run_try_repo(tempdir_factory, verbose=True)
|
||||||
|
config, rest = _get_out(cap_out)
|
||||||
|
assert re.match(
|
||||||
|
'^repos:\n'
|
||||||
|
'- repo: .+\n'
|
||||||
|
' sha: .+\n'
|
||||||
|
' hooks:\n'
|
||||||
|
' - id: bash_hook\n'
|
||||||
|
' - id: bash_hook2\n'
|
||||||
|
' - id: bash_hook3\n$',
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
assert rest == (
|
||||||
|
'[bash_hook] Bash hook................................(no files to check)Skipped\n' # noqa
|
||||||
|
'[bash_hook2] Bash hook...................................................Passed\n' # noqa
|
||||||
|
'hookid: bash_hook2\n'
|
||||||
|
'\n'
|
||||||
|
'test-file\n'
|
||||||
|
'\n'
|
||||||
|
'[bash_hook3] Bash hook...............................(no files to check)Skipped\n' # noqa
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_try_repo_with_specific_hook(cap_out, tempdir_factory):
|
||||||
|
_run_try_repo(tempdir_factory, hook='bash_hook', verbose=True)
|
||||||
|
config, rest = _get_out(cap_out)
|
||||||
|
assert re.match(
|
||||||
|
'^repos:\n'
|
||||||
|
'- repo: .+\n'
|
||||||
|
' sha: .+\n'
|
||||||
|
' hooks:\n'
|
||||||
|
' - id: bash_hook\n$',
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
assert rest == '[bash_hook] Bash hook................................(no files to check)Skipped\n' # noqa
|
||||||
|
|
@ -92,12 +92,18 @@ def test_help_other_command(
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('command', CMDS)
|
@pytest.mark.parametrize('command', CMDS)
|
||||||
def test_install_command(command, mock_commands):
|
def test_all_cmds(command, mock_commands):
|
||||||
main.main((command,))
|
main.main((command,))
|
||||||
assert getattr(mock_commands, command.replace('-', '_')).call_count == 1
|
assert getattr(mock_commands, command.replace('-', '_')).call_count == 1
|
||||||
assert_only_one_mock_called(mock_commands)
|
assert_only_one_mock_called(mock_commands)
|
||||||
|
|
||||||
|
|
||||||
|
def test_try_repo():
|
||||||
|
with mock.patch.object(main, 'try_repo') as patch:
|
||||||
|
main.main(('try-repo', '.'))
|
||||||
|
assert patch.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_help_cmd_in_empty_directory(
|
def test_help_cmd_in_empty_directory(
|
||||||
mock_commands,
|
mock_commands,
|
||||||
tempdir_factory,
|
tempdir_factory,
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ import tarfile
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from pre_commit import git
|
||||||
from pre_commit import make_archives
|
from pre_commit import make_archives
|
||||||
from pre_commit.util import cmd_output
|
from pre_commit.util import cmd_output
|
||||||
from pre_commit.util import cwd
|
from pre_commit.util import cwd
|
||||||
from testing.fixtures import git_dir
|
from testing.fixtures import git_dir
|
||||||
from testing.util import get_head_sha
|
|
||||||
from testing.util import skipif_slowtests_false
|
from testing.util import skipif_slowtests_false
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ def test_make_archive(tempdir_factory):
|
||||||
cmd_output('git', 'add', '.')
|
cmd_output('git', 'add', '.')
|
||||||
cmd_output('git', 'commit', '-m', 'foo')
|
cmd_output('git', 'commit', '-m', 'foo')
|
||||||
# We'll use this sha
|
# We'll use this sha
|
||||||
head_sha = get_head_sha('.')
|
head_sha = git.head_sha('.')
|
||||||
# And check that this file doesn't exist
|
# And check that this file doesn't exist
|
||||||
open('bar', 'a').close()
|
open('bar', 'a').close()
|
||||||
cmd_output('git', 'add', '.')
|
cmd_output('git', 'add', '.')
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,16 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from pre_commit import git
|
||||||
from pre_commit.manifest import Manifest
|
from pre_commit.manifest import Manifest
|
||||||
from testing.fixtures import make_repo
|
from testing.fixtures import make_repo
|
||||||
from testing.util import get_head_sha
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.yield_fixture
|
@pytest.yield_fixture
|
||||||
def manifest(store, tempdir_factory):
|
def manifest(store, tempdir_factory):
|
||||||
path = make_repo(tempdir_factory, 'script_hooks_repo')
|
path = make_repo(tempdir_factory, 'script_hooks_repo')
|
||||||
repo_path = store.clone(path, get_head_sha(path))
|
repo_path = store.clone(path, git.head_sha(path))
|
||||||
yield Manifest(repo_path, path)
|
yield Manifest(repo_path)
|
||||||
|
|
||||||
|
|
||||||
def test_manifest_contents(manifest):
|
def test_manifest_contents(manifest):
|
||||||
|
|
@ -62,8 +62,8 @@ def test_hooks(manifest):
|
||||||
|
|
||||||
def test_default_python_language_version(store, tempdir_factory):
|
def test_default_python_language_version(store, tempdir_factory):
|
||||||
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
||||||
repo_path = store.clone(path, get_head_sha(path))
|
repo_path = store.clone(path, git.head_sha(path))
|
||||||
manifest = Manifest(repo_path, path)
|
manifest = Manifest(repo_path)
|
||||||
|
|
||||||
# This assertion is difficult as it is version dependent, just assert
|
# This assertion is difficult as it is version dependent, just assert
|
||||||
# that it is *something*
|
# that it is *something*
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@ import mock
|
||||||
import pytest
|
import pytest
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from pre_commit import git
|
||||||
from pre_commit.store import _get_default_directory
|
from pre_commit.store import _get_default_directory
|
||||||
from pre_commit.store import Store
|
from pre_commit.store import Store
|
||||||
from pre_commit.util import cmd_output
|
from pre_commit.util import cmd_output
|
||||||
from pre_commit.util import cwd
|
from pre_commit.util import cwd
|
||||||
from pre_commit.util import rmtree
|
from pre_commit.util import rmtree
|
||||||
from testing.fixtures import git_dir
|
from testing.fixtures import git_dir
|
||||||
from testing.util import get_head_sha
|
|
||||||
|
|
||||||
|
|
||||||
def test_our_session_fixture_works():
|
def test_our_session_fixture_works():
|
||||||
|
|
@ -91,7 +91,7 @@ def test_clone(store, tempdir_factory, log_info_mock):
|
||||||
path = git_dir(tempdir_factory)
|
path = git_dir(tempdir_factory)
|
||||||
with cwd(path):
|
with cwd(path):
|
||||||
cmd_output('git', 'commit', '--allow-empty', '-m', 'foo')
|
cmd_output('git', 'commit', '--allow-empty', '-m', 'foo')
|
||||||
sha = get_head_sha(path)
|
sha = git.head_sha(path)
|
||||||
cmd_output('git', 'commit', '--allow-empty', '-m', 'bar')
|
cmd_output('git', 'commit', '--allow-empty', '-m', 'bar')
|
||||||
|
|
||||||
ret = store.clone(path, sha)
|
ret = store.clone(path, sha)
|
||||||
|
|
@ -107,7 +107,7 @@ def test_clone(store, tempdir_factory, log_info_mock):
|
||||||
_, dirname = os.path.split(ret)
|
_, dirname = os.path.split(ret)
|
||||||
assert dirname.startswith('repo')
|
assert dirname.startswith('repo')
|
||||||
# Should be checked out to the sha we specified
|
# Should be checked out to the sha we specified
|
||||||
assert get_head_sha(ret) == sha
|
assert git.head_sha(ret) == sha
|
||||||
|
|
||||||
# Assert there's an entry in the sqlite db for this
|
# Assert there's an entry in the sqlite db for this
|
||||||
with sqlite3.connect(store.db_path) as db:
|
with sqlite3.connect(store.db_path) as db:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue