From 26d563ee2056c873489ee8dca9682336993aec1a Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 23 Mar 2014 22:50:01 -0700 Subject: [PATCH] Autoupdate works. Closes #44. --- pre_commit/commands.py | 43 +++++++++++++++++++++++++- pre_commit/constants.py | 6 ++++ pre_commit/yaml_extensions.py | 2 +- tests/commands_test.py | 57 ++++++++++++++++++++++++++++++++--- tests/yaml_extensions_test.py | 3 +- 5 files changed, 104 insertions(+), 7 deletions(-) diff --git a/pre_commit/commands.py b/pre_commit/commands.py index 7353c8e3..456aa9b0 100644 --- a/pre_commit/commands.py +++ b/pre_commit/commands.py @@ -6,8 +6,12 @@ import pkg_resources import stat from plumbum import local +import pre_commit.constants as C +from pre_commit.clientlib.validate_config import load_config from pre_commit.ordereddict import OrderedDict from pre_commit.repository import Repository +from pre_commit.yaml_extensions import ordered_dump +from pre_commit.yaml_extensions import ordered_load def install(runner): @@ -75,4 +79,41 @@ def _update_repository(repo_config): def autoupdate(runner): """Auto-update the pre-commit config to the latest versions of repos.""" - pass + retv = 0 + output_configs = [] + changed = False + + input_configs = load_config( + runner.config_file_path, + load_strategy=ordered_load, + ) + + for repo_config in input_configs: + print('Updating {0}...'.format(repo_config['repo']), end='') + try: + new_repo_config = _update_repository(repo_config) + except RepositoryCannotBeUpdatedError as e: + print(e.args[0]) + output_configs.append(repo_config) + retv = 1 + continue + + if new_repo_config['sha'] != repo_config['sha']: + changed = True + print( + 'updating {0} -> {1}.'.format( + repo_config['sha'], new_repo_config['sha'], + ) + ) + output_configs.append(new_repo_config) + else: + print('already up to date.') + output_configs.append(repo_config) + + if changed: + with open(runner.config_file_path, 'w') as config_file: + config_file.write( + ordered_dump(output_configs, **C.YAML_DUMP_KWARGS) + ) + + return retv diff --git a/pre_commit/constants.py b/pre_commit/constants.py index dd4eed2c..c5a81eb5 100644 --- a/pre_commit/constants.py +++ b/pre_commit/constants.py @@ -10,3 +10,9 @@ SUPPORTED_LANGUAGES = set([ 'ruby', 'node', ]) + + +YAML_DUMP_KWARGS = { + 'default_flow_style': False, + 'indent': 4, +} diff --git a/pre_commit/yaml_extensions.py b/pre_commit/yaml_extensions.py index 70bcec72..af19a646 100644 --- a/pre_commit/yaml_extensions.py +++ b/pre_commit/yaml_extensions.py @@ -18,7 +18,7 @@ def ordered_load(s): def ordered_dump(s, **kwargs): - class OrderedDumper(yaml.dumper.Dumper): pass + class OrderedDumper(yaml.dumper.SafeDumper): pass def dict_representer(dumper, data): return dumper.represent_mapping( yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, diff --git a/tests/commands_test.py b/tests/commands_test.py index 201c83dc..7cb54d76 100644 --- a/tests/commands_test.py +++ b/tests/commands_test.py @@ -8,15 +8,18 @@ import shutil import stat from plumbum import local +import pre_commit.constants as C from pre_commit import git from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA from pre_commit.clientlib.validate_config import validate_config_extra +from pre_commit.commands import autoupdate from pre_commit.commands import install from pre_commit.commands import RepositoryCannotBeUpdatedError from pre_commit.commands import uninstall from pre_commit.commands import _update_repository from pre_commit.ordereddict import OrderedDict from pre_commit.runner import Runner +from pre_commit.yaml_extensions import ordered_dump from testing.auto_namedtuple import auto_namedtuple from testing.util import get_resource_path @@ -54,10 +57,16 @@ def up_to_date_repo(python_hooks_repo): config = OrderedDict(( ('repo', python_hooks_repo), ('sha', git.get_head_sha(python_hooks_repo)), - ('hooks', [{'id': 'foo', 'files': ''}]), + ('hooks', [OrderedDict((('id', 'foo'), ('files', '')))]), )) jsonschema.validate([config], CONFIG_JSON_SCHEMA) validate_config_extra([config]) + + with open(os.path.join(python_hooks_repo, C.CONFIG_FILE), 'w') as file_obj: + file_obj.write( + ordered_dump([config], **C.YAML_DUMP_KWARGS) + ) + yield auto_namedtuple( repo_config=config, python_hooks_repo=python_hooks_repo, @@ -70,17 +79,32 @@ def test_up_to_date_repo(up_to_date_repo): assert ret['sha'] == input_sha +def test_autoupdate_up_to_date_repo(up_to_date_repo): + before = open(C.CONFIG_FILE).read() + runner = Runner(up_to_date_repo.python_hooks_repo) + ret = autoupdate(runner) + after = open(C.CONFIG_FILE).read() + assert ret == 0 + assert before == after + + @pytest.yield_fixture def out_of_date_repo(python_hooks_repo): config = OrderedDict(( ('repo', python_hooks_repo), ('sha', git.get_head_sha(python_hooks_repo)), - ('hooks', [{'id': 'foo', 'files': ''}]), + ('hooks', [OrderedDict((('id', 'foo'), ('files', '')))]), )) jsonschema.validate([config], CONFIG_JSON_SCHEMA) validate_config_extra([config]) local['git']['commit', '--allow-empty', '-m', 'foo']() head_sha = git.get_head_sha(python_hooks_repo) + + with open(os.path.join(python_hooks_repo, C.CONFIG_FILE), 'w') as file_obj: + file_obj.write( + ordered_dump([config], **C.YAML_DUMP_KWARGS) + ) + yield auto_namedtuple( repo_config=config, head_sha=head_sha, @@ -93,18 +117,34 @@ def test_out_of_date_repo(out_of_date_repo): assert ret['sha'] == out_of_date_repo.head_sha +def test_autoupdate_out_of_date_repo(out_of_date_repo): + before = open(C.CONFIG_FILE).read() + runner = Runner(out_of_date_repo.python_hooks_repo) + ret = autoupdate(runner) + after = open(C.CONFIG_FILE).read() + assert ret == 0 + assert before != after + assert out_of_date_repo.head_sha in after + + @pytest.yield_fixture def hook_disappearing_repo(python_hooks_repo): config = OrderedDict(( ('repo', python_hooks_repo), ('sha', git.get_head_sha(python_hooks_repo)), - ('hooks', [{'id': 'foo', 'files': ''}]), + ('hooks', [OrderedDict((('id', 'foo'), ('files', '')))]), )) jsonschema.validate([config], CONFIG_JSON_SCHEMA) validate_config_extra([config]) - shutil.copy(get_resource_path('manifest_without_foo.yaml'), 'manifest.yaml') + shutil.copy(get_resource_path('manifest_without_foo.yaml'), C.MANIFEST_FILE) local['git']['add', '.']() local['git']['commit', '-m', 'Remove foo']() + + with open(os.path.join(python_hooks_repo, C.CONFIG_FILE), 'w') as file_obj: + file_obj.write( + ordered_dump([config], **C.YAML_DUMP_KWARGS) + ) + yield auto_namedtuple( repo_config=config, python_hooks_repo=python_hooks_repo, @@ -114,3 +154,12 @@ def hook_disappearing_repo(python_hooks_repo): def test_hook_disppearing_repo_raises(hook_disappearing_repo): with pytest.raises(RepositoryCannotBeUpdatedError): _update_repository(hook_disappearing_repo.repo_config) + + +def test_autoupdate_hook_disappearing_repo(hook_disappearing_repo): + before = open(C.CONFIG_FILE).read() + runner = Runner(hook_disappearing_repo.python_hooks_repo) + ret = autoupdate(runner) + after = open(C.CONFIG_FILE).read() + assert ret == 1 + assert before == after diff --git a/tests/yaml_extensions_test.py b/tests/yaml_extensions_test.py index c60c1aef..098d9300 100644 --- a/tests/yaml_extensions_test.py +++ b/tests/yaml_extensions_test.py @@ -1,4 +1,5 @@ +import pre_commit.constants as C from pre_commit.ordereddict import OrderedDict from pre_commit.yaml_extensions import ordered_dump from pre_commit.yaml_extensions import ordered_load @@ -25,7 +26,7 @@ def test_ordered_dump(): OrderedDict( (('a', 'herp'), ('c', 'derp'), ('b', 'harp'), ('d', 'darp')) ), - default_flow_style=False, + **C.YAML_DUMP_KWARGS ) assert ret == ( 'a: herp\n'