Add support for commit-msg git hook

This commit is contained in:
Anthony Sottile 2017-07-22 15:16:12 -07:00
parent a6a4762f0d
commit d0b268c813
9 changed files with 107 additions and 57 deletions

View file

@ -56,7 +56,7 @@ def test_install_pre_commit(tempdir_factory):
expected_contents = io.open(pre_commit_script).read().format(
sys_executable=sys.executable,
hook_type='pre-commit',
pre_push='',
hook_specific='',
skip_on_missing_conf='false',
)
assert pre_commit_contents == expected_contents
@ -71,7 +71,7 @@ def test_install_pre_commit(tempdir_factory):
expected_contents = io.open(pre_commit_script).read().format(
sys_executable=sys.executable,
hook_type='pre-push',
pre_push=pre_push_template_contents,
hook_specific=pre_push_template_contents,
skip_on_missing_conf='false',
)
assert pre_push_contents == expected_contents
@ -118,10 +118,11 @@ def test_uninstall(tempdir_factory):
def _get_commit_output(tempdir_factory, touch_file='foo', **kwargs):
commit_msg = kwargs.pop('commit_msg', 'Commit!')
open(touch_file, 'a').close()
cmd_output('git', 'add', touch_file)
return cmd_output_mocked_pre_commit_home(
'git', 'commit', '-am', 'Commit!', '--allow-empty',
'git', 'commit', '-am', commit_msg, '--allow-empty',
# git commit puts pre-commit to stderr
stderr=subprocess.STDOUT,
retcode=None,
@ -560,6 +561,24 @@ def test_pre_push_integration_empty_push(tempdir_factory):
assert retc == 0
def test_commit_msg_integration_failing(commit_msg_repo, tempdir_factory):
install(Runner(commit_msg_repo, C.CONFIG_FILE), hook_type='commit-msg')
retc, out = _get_commit_output(tempdir_factory)
assert retc == 1
assert out.startswith('Must have "Signed off by:"...')
assert out.strip().endswith('...Failed')
def test_commit_msg_integration_passing(commit_msg_repo, tempdir_factory):
install(Runner(commit_msg_repo, C.CONFIG_FILE), hook_type='commit-msg')
msg = 'Hi\nSigned off by: me, lol'
retc, out = _get_commit_output(tempdir_factory, commit_msg=msg)
assert retc == 0
first_line = out.splitlines()[0]
assert first_line.startswith('Must have "Signed off by:"...')
assert first_line.endswith('...Passed')
def test_install_disallow_mising_config(tempdir_factory):
path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
with cwd(path):

View file

@ -60,6 +60,7 @@ def _get_opts(
allow_unstaged_config=False,
hook_stage='commit',
show_diff_on_failure=False,
commit_msg_filename='',
):
# These are mutually exclusive
assert not (all_files and files)
@ -75,6 +76,7 @@ def _get_opts(
allow_unstaged_config=allow_unstaged_config,
hook_stage=hook_stage,
show_diff_on_failure=show_diff_on_failure,
commit_msg_filename=commit_msg_filename,
)
@ -572,40 +574,7 @@ def test_lots_of_files(mock_out_store_directory, tempdir_factory):
)
@pytest.mark.parametrize(
(
'hook_stage', 'stage_for_first_hook', 'stage_for_second_hook',
'expected_output',
),
(
('push', ['commit'], ['commit'], [b'', b'']),
(
'push', ['commit', 'push'], ['commit', 'push'],
[b'hook 1', b'hook 2'],
),
('push', [], [], [b'hook 1', b'hook 2']),
('push', [], ['commit'], [b'hook 1', b'']),
('push', ['push'], ['commit'], [b'hook 1', b'']),
('push', ['commit'], ['push'], [b'', b'hook 2']),
(
'commit', ['commit', 'push'], ['commit', 'push'],
[b'hook 1', b'hook 2'],
),
('commit', ['commit'], ['commit'], [b'hook 1', b'hook 2']),
('commit', [], [], [b'hook 1', b'hook 2']),
('commit', [], ['commit'], [b'hook 1', b'hook 2']),
('commit', ['push'], ['commit'], [b'', b'hook 2']),
('commit', ['commit'], ['push'], [b'hook 1', b'']),
),
)
def test_local_hook_for_stages(
cap_out,
repo_with_passing_hook, mock_out_store_directory,
stage_for_first_hook,
stage_for_second_hook,
hook_stage,
expected_output,
):
def test_push_hook(cap_out, repo_with_passing_hook, mock_out_store_directory):
config = OrderedDict((
('repo', 'local'),
(
@ -613,36 +582,60 @@ def test_local_hook_for_stages(
OrderedDict((
('id', 'flake8'),
('name', 'hook 1'),
('entry', 'python -m flake8.__main__'),
('entry', "'{}' -m flake8".format(sys.executable)),
('language', 'system'),
('files', r'\.py$'),
('stages', stage_for_first_hook),
)), OrderedDict((
('types', ['python']),
('stages', ['commit']),
)),
OrderedDict((
('id', 'do_not_commit'),
('name', 'hook 2'),
('entry', 'DO NOT COMMIT'),
('language', 'pcre'),
('files', '^(.*)$'),
('stages', stage_for_second_hook),
('types', ['text']),
('stages', ['push']),
)),
),
),
))
add_config_to_repo(repo_with_passing_hook, config)
with io.open('dummy.py', 'w') as staged_file:
staged_file.write('"""TODO: something"""\n')
open('dummy.py', 'a').close()
cmd_output('git', 'add', 'dummy.py')
_test_run(
cap_out,
repo_with_passing_hook,
{'hook_stage': hook_stage},
expected_outputs=expected_output,
{'hook_stage': 'commit'},
expected_outputs=[b'hook 1'],
expected_ret=0,
stage=False,
)
_test_run(
cap_out,
repo_with_passing_hook,
{'hook_stage': 'push'},
expected_outputs=[b'hook 2'],
expected_ret=0,
stage=False,
)
def test_commit_msg_hook(cap_out, commit_msg_repo, mock_out_store_directory):
filename = '.git/COMMIT_EDITMSG'
with io.open(filename, 'w') as f:
f.write('This is the commit message')
_test_run(
cap_out,
commit_msg_repo,
{'hook_stage': 'commit-msg', 'commit_msg_filename': filename},
expected_outputs=[b'Must have "Signed off by:"', b'Failed'],
expected_ret=1,
stage=False,
)
def test_local_hook_passes(
cap_out, repo_with_passing_hook, mock_out_store_directory,
@ -654,7 +647,7 @@ def test_local_hook_passes(
OrderedDict((
('id', 'flake8'),
('name', 'flake8'),
('entry', 'python -m flake8.__main__'),
('entry', "'{}' -m flake8".format(sys.executable)),
('language', 'system'),
('files', r'\.py$'),
)), OrderedDict((

View file

@ -1,6 +1,7 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import collections
import functools
import io
import logging
@ -20,6 +21,7 @@ from pre_commit.util import cmd_output
from pre_commit.util import cwd
from testing.fixtures import git_dir
from testing.fixtures import make_consuming_repo
from testing.fixtures import write_config
@pytest.yield_fixture
@ -92,6 +94,29 @@ def in_conflicting_submodule(tempdir_factory):
yield
@pytest.fixture
def commit_msg_repo(tempdir_factory):
path = git_dir(tempdir_factory)
config = collections.OrderedDict((
('repo', 'local'),
(
'hooks',
[collections.OrderedDict((
('id', 'must-have-signoff'),
('name', 'Must have "Signed off by:"'),
('entry', 'grep -q "Signed off by:"'),
('language', 'system'),
('stages', ['commit-msg']),
))],
),
))
write_config(path, config)
with cwd(path):
cmd_output('git', 'add', '.')
cmd_output('git', 'commit', '-m', 'add hooks')
yield path
@pytest.yield_fixture(autouse=True, scope='session')
def dont_write_to_home_directory():
"""pre_commit.store.Store will by default write to the home directory