Implement check-useless-excludes meta hook

This commit is contained in:
Paul Hooijenga 2017-10-23 14:29:08 +02:00
parent 88c676a7c1
commit 8df11ee7aa
4 changed files with 118 additions and 8 deletions

View file

View file

@ -0,0 +1,41 @@
import re
import sys
import pre_commit.constants as C
from pre_commit.clientlib import load_config
from pre_commit.git import get_all_files
def exclude_matches_any(filenames, include, exclude):
include_re, exclude_re = re.compile(include), re.compile(exclude)
for filename in filenames:
if include_re.search(filename) and exclude_re.search(filename):
return True
return False
def check_useless_excludes(config_file=None):
config = load_config(config_file or C.CONFIG_FILE)
files = get_all_files()
useless_excludes = False
exclude = config.get('exclude')
if exclude != '^$' and not exclude_matches_any(files, '', exclude):
print('The global exclude pattern does not match any files')
useless_excludes = True
for repo in config['repos']:
for hook in repo['hooks']:
include, exclude = hook.get('files', ''), hook.get('exclude')
if exclude and not exclude_matches_any(files, include, exclude):
print(
'The exclude pattern for {} does not match any files'
.format(hook['id'])
)
useless_excludes = True
return useless_excludes
if __name__ == '__main__':
sys.exit(check_useless_excludes())

View file

@ -4,6 +4,7 @@ import io
import json import json
import logging import logging
import os import os
import pipes
import shutil import shutil
import sys import sys
from collections import defaultdict from collections import defaultdict
@ -247,13 +248,16 @@ class LocalRepository(Repository):
class MetaRepository(LocalRepository): class MetaRepository(LocalRepository):
# Note: the hook `entry` is passed through `shlex.split()` by the command
# runner, so to prevent issues with spaces and backslashes (on Windows) it
# must be quoted here.
meta_hooks = { meta_hooks = {
'test-hook': { 'check-useless-excludes': {
'name': 'Test Hook', 'name': 'Check for useless excludes',
'files': '', 'files': '.pre-commit-config.yaml',
'language': 'system', 'language': 'system',
'entry': 'echo "Hello World!"', 'entry': pipes.quote(sys.executable),
'always_run': True, 'args': ['-m', 'pre_commit.meta_hooks.check_useless_excludes'],
}, },
} }

View file

@ -653,7 +653,7 @@ def test_meta_hook_passes(
( (
'hooks', ( 'hooks', (
OrderedDict(( OrderedDict((
('id', 'test-hook'), ('id', 'check-useless-excludes'),
)), )),
), ),
), ),
@ -663,13 +663,78 @@ def test_meta_hook_passes(
_test_run( _test_run(
cap_out, cap_out,
repo_with_passing_hook, repo_with_passing_hook,
opts={'verbose': True}, opts={},
expected_outputs=[b'Hello World!'], expected_outputs=[b'Check for useless excludes'],
expected_ret=0, expected_ret=0,
stage=False, stage=False,
) )
def test_useless_exclude_global(
cap_out, repo_with_passing_hook, mock_out_store_directory,
):
config = OrderedDict((
('exclude', 'foo'),
(
'repos', [
OrderedDict((
('repo', 'meta'),
(
'hooks', (
OrderedDict((
('id', 'check-useless-excludes'),
)),
),
),
)),
],
),
))
add_config_to_repo(repo_with_passing_hook, config)
_test_run(
cap_out,
repo_with_passing_hook,
opts={'all_files': True},
expected_outputs=[
b'Check for useless excludes',
b'The global exclude pattern does not match any files',
],
expected_ret=1,
stage=False,
)
def test_useless_exclude_for_hook(
cap_out, repo_with_passing_hook, mock_out_store_directory,
):
config = OrderedDict((
('repo', 'meta'),
(
'hooks', (
OrderedDict((
('id', 'check-useless-excludes'),
('exclude', 'foo'),
)),
),
),
))
add_config_to_repo(repo_with_passing_hook, config)
_test_run(
cap_out,
repo_with_passing_hook,
opts={'all_files': True},
expected_outputs=[
b'Check for useless excludes',
b'The exclude pattern for check-useless-excludes '
b'does not match any files',
],
expected_ret=1,
stage=False,
)
@pytest.yield_fixture @pytest.yield_fixture
def modified_config_repo(repo_with_passing_hook): def modified_config_repo(repo_with_passing_hook):
with modify_config(repo_with_passing_hook, commit=False) as config: with modify_config(repo_with_passing_hook, commit=False) as config: