From 2ebba7994db26c10f4d10937bba84339705d24a1 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 13 Mar 2014 17:34:24 -0700 Subject: [PATCH] Added config validator and tests for it --- .pre-commit-config.yaml | 8 +++ example_pre-commit-config.yaml | 14 ++++ pre_commit/clientlib/validate_config.py | 72 +++++++++++++++++++ pre_commit/entry_points.py | 4 +- setup.py | 1 + tests/clientlib/validate_config_test.py | 61 ++++++++++++++++ tests/data/valid_yaml_but_invalid_config.yaml | 11 +++ 7 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 .pre-commit-config.yaml create mode 100644 example_pre-commit-config.yaml create mode 100644 pre_commit/clientlib/validate_config.py create mode 100644 tests/clientlib/validate_config_test.py create mode 100644 tests/data/valid_yaml_but_invalid_config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..79f34579 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,8 @@ + +- + repo: git@github.com:pre-commit/pre-commit-hooks + sha: cd74dc150c142c3be70b24eaf0b02cae9d235f37 + hooks: + - + id: pyflakes + files: '*.py' \ No newline at end of file diff --git a/example_pre-commit-config.yaml b/example_pre-commit-config.yaml new file mode 100644 index 00000000..06aa4f12 --- /dev/null +++ b/example_pre-commit-config.yaml @@ -0,0 +1,14 @@ + +- + repo: git@github.com:pre-commit/pre-commit-hooks + sha: cd74dc150c142c3be70b24eaf0b02cae9d235f37 + hooks: + - + id: pyflakes + files: '*.py' + - + id: jslint + files: '*.js' + - + id: trim_trailing_whitespace + files: '*.py' \ No newline at end of file diff --git a/pre_commit/clientlib/validate_config.py b/pre_commit/clientlib/validate_config.py new file mode 100644 index 00000000..241816ba --- /dev/null +++ b/pre_commit/clientlib/validate_config.py @@ -0,0 +1,72 @@ + +from __future__ import print_function + +import argparse + +import pre_commit.constants as C +from pre_commit.clientlib.validate_base import get_validator + + +class InvalidConfigError(ValueError): pass + + +CONFIG_JSON_SCHEMA = { + 'type': 'array', + 'minItems': 1, + 'items': { + 'type': 'object', + 'properties': { + 'repo': {'type': 'string'}, + 'sha': {'type': 'string'}, + 'hooks': { + 'type': 'array', + 'minItems': 1, + 'items': { + 'type': 'object', + 'properties': { + 'id': {'type': 'string'}, + 'files': {'type': 'string'}, + 'args': { + 'type': 'array', + 'minItems': 1, + 'items': {'type': 'string'}, + }, + }, + 'required': ['id', 'files'], + } + } + }, + 'required': ['repo', 'sha', 'hooks'], + } +} + + +validate_manifest = get_validator( + C.CONFIG_FILE, + CONFIG_JSON_SCHEMA, + InvalidConfigError, +) + + +def run(argv): + parser = argparse.ArgumentParser() + parser.add_argument( + 'filename', + nargs='?', default=None, + help='Config filename. Defaults to {0} at root of git repo'.format( + C.CONFIG_FILE, + ) + ) + args = parser.parse_args(argv) + + try: + validate_manifest(args.filename) + except InvalidConfigError as e: + print(e.args[0]) + # If we have more than one exception argument print the stringified + # version + if len(e.args) > 1: + print(str(e.args[1])) + return 1 + + return 0 \ No newline at end of file diff --git a/pre_commit/entry_points.py b/pre_commit/entry_points.py index 4d508256..4f448ca1 100644 --- a/pre_commit/entry_points.py +++ b/pre_commit/entry_points.py @@ -1,6 +1,7 @@ import functools +import pre_commit.clientlib.validate_config import pre_commit.clientlib.validate_manifest import pre_commit.run @@ -20,4 +21,5 @@ def make_entry_point(entry_point_func): pre_commit_func = make_entry_point(pre_commit.run.run) -validate_manifest_func = make_entry_point(pre_commit.clientlib.validate_manifest.run) \ No newline at end of file +validate_manifest_func = make_entry_point(pre_commit.clientlib.validate_manifest.run) +validate_config_func = make_entry_point(pre_commit.clientlib.validate_config.run) \ No newline at end of file diff --git a/setup.py b/setup.py index 449342ce..cb17fcdf 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ setup( entry_points={ 'console_scripts': [ 'pre-commit = pre_commit.entry_points:pre_commit_func', + 'validate-config = pre_commit.entry_points:validate_config_func', 'validate-manifest = pre_commit.entry_points:validate_manifest_func', ], } diff --git a/tests/clientlib/validate_config_test.py b/tests/clientlib/validate_config_test.py new file mode 100644 index 00000000..7f8baf16 --- /dev/null +++ b/tests/clientlib/validate_config_test.py @@ -0,0 +1,61 @@ + +import jsonschema +import jsonschema.exceptions +import pytest + +from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA +from pre_commit.clientlib.validate_config import run + + +def test_returns_0_for_valid_config(): + assert run(['example_pre-commit-config.yaml']) == 0 + + +def test_returns_0_for_out_manifest(): + assert run([]) == 0 + + +def test_returns_1_for_failing(): + assert run(['tests/data/valid_yaml_but_invalid_config.yaml']) == 1 + +def is_valid_according_to_schema(obj, schema): + try: + jsonschema.validate(obj, schema) + return True + except jsonschema.exceptions.ValidationError: + return False + + +@pytest.mark.parametrize(('manifest_obj', 'expected'), ( + ([], False), + ( + [{ + 'repo': 'git@github.com:pre-commit/pre-commit-hooks', + 'sha': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37', + 'hooks': [ + { + 'id': 'pyflakes', + 'files': '*.py', + } + ] + }], + True, + ), + ( + [{ + 'repo': 'git@github.com:pre-commit/pre-commit-hooks', + 'sha': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37', + 'hooks': [ + { + 'id': 'pyflakes', + 'files': '*.py', + 'args': ['foo', 'bar', 'baz'], + } + ] + }], + True, + ), +)) +def test_is_valid_according_to_schema(manifest_obj, expected): + ret = is_valid_according_to_schema(manifest_obj, CONFIG_JSON_SCHEMA) + assert ret is expected \ No newline at end of file diff --git a/tests/data/valid_yaml_but_invalid_config.yaml b/tests/data/valid_yaml_but_invalid_config.yaml new file mode 100644 index 00000000..e3cfdff5 --- /dev/null +++ b/tests/data/valid_yaml_but_invalid_config.yaml @@ -0,0 +1,11 @@ + +- + repo: git@github.com:pre-commit/pre-commit-hooks + hooks: + - + id: pyflakes + - + id: jslint + - + id: trim_trailing_whitespace + files: '*.py' \ No newline at end of file