diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py index 2f16650a..c16a3ace 100644 --- a/pre_commit/clientlib.py +++ b/pre_commit/clientlib.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import argparse import functools +import logging import pipes import sys @@ -15,6 +16,8 @@ from pre_commit.error_handler import FatalError from pre_commit.languages.all import all_languages from pre_commit.util import parse_version +logger = logging.getLogger('pre_commit') + def check_type_tag(tag): if tag not in ALL_TAGS: @@ -106,6 +109,8 @@ META = 'meta' class MigrateShaToRev(object): + key = 'rev' + @staticmethod def _cond(key): return cfgv.Conditional( @@ -144,6 +149,14 @@ def _entry(modname): ) +def warn_unknown_keys(extra, orig_keys): + logger.warning( + 'Unexpected config key(s): {}'.format( + ', '.join(sorted(extra)), + ), + ) + + _meta = ( ( 'check-hooks-apply', ( @@ -222,6 +235,10 @@ CONFIG_REPO_DICT = cfgv.Map( ), MigrateShaToRev(), + cfgv.WarnAdditionalKeys( + ('repo', 'rev', 'hooks'), + warn_unknown_keys, + ), ) DEFAULT_LANGUAGE_VERSION = cfgv.Map( 'DefaultLanguageVersion', None, @@ -247,6 +264,17 @@ CONFIG_SCHEMA = cfgv.Map( cfgv.check_and(cfgv.check_string, check_min_version), '0', ), + cfgv.WarnAdditionalKeys( + ( + 'repos', + 'default_language_version', + 'default_stages', + 'exclude', + 'fail_fast', + 'minimum_pre_commit_version', + ), + warn_unknown_keys, + ), ) diff --git a/tests/clientlib_test.py b/tests/clientlib_test.py index 2cdc1528..13b42a59 100644 --- a/tests/clientlib_test.py +++ b/tests/clientlib_test.py @@ -1,5 +1,7 @@ from __future__ import unicode_literals +import logging + import cfgv import pytest @@ -116,6 +118,48 @@ def test_validate_config_old_list_format_ok(tmpdir): assert not validate_config_main((f.strpath,)) +def test_validate_warn_on_unknown_keys_at_repo_level(tmpdir, caplog): + f = tmpdir.join('cfg.yaml') + f.write( + '- repo: https://gitlab.com/pycqa/flake8\n' + ' rev: 3.7.7\n' + ' hooks:\n' + ' - id: flake8\n' + ' args: [--some-args]\n', + ) + ret_val = validate_config_main((f.strpath,)) + assert not ret_val + assert caplog.record_tuples == [ + ( + 'pre_commit', + logging.WARNING, + 'Unexpected config key(s): args', + ), + ] + + +def test_validate_warn_on_unknown_keys_at_top_level(tmpdir, caplog): + f = tmpdir.join('cfg.yaml') + f.write( + 'repos:\n' + '- repo: https://gitlab.com/pycqa/flake8\n' + ' rev: 3.7.7\n' + ' hooks:\n' + ' - id: flake8\n' + 'foo:\n' + ' id: 1.0.0\n', + ) + ret_val = validate_config_main((f.strpath,)) + assert not ret_val + assert caplog.record_tuples == [ + ( + 'pre_commit', + logging.WARNING, + 'Unexpected config key(s): foo', + ), + ] + + @pytest.mark.parametrize('fn', (validate_config_main, validate_manifest_main)) def test_mains_not_ok(tmpdir, fn): not_yaml = tmpdir.join('f.notyaml') @@ -261,3 +305,12 @@ def test_minimum_pre_commit_version_failing(): def test_minimum_pre_commit_version_passing(): cfg = {'repos': [], 'minimum_pre_commit_version': '0'} cfgv.validate(cfg, CONFIG_SCHEMA) + + +@pytest.mark.parametrize('schema', (CONFIG_SCHEMA, CONFIG_REPO_DICT)) +def test_warn_additional(schema): + allowed_keys = {item.key for item in schema.items if hasattr(item, 'key')} + warn_additional, = [ + x for x in schema.items if isinstance(x, cfgv.WarnAdditionalKeys) + ] + assert allowed_keys == set(warn_additional.keys)