Skip to content

Commit ce307a1

Browse files
committed
Add an option to require a specific pre-commit version
1 parent c24f78b commit ce307a1

5 files changed

Lines changed: 68 additions & 0 deletions

File tree

pre_commit/clientlib/validate_manifest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class InvalidManifestError(ValueError):
2323
'exclude': {'type': 'string', 'default': '^$'},
2424
'language': {'type': 'string'},
2525
'language_version': {'type': 'string', 'default': 'default'},
26+
'minimum_pre_commit_version': {
27+
'type': 'string', 'default': '0.0.0',
28+
},
2629
'files': {'type': 'string'},
2730
'stages': {
2831
'type': 'array',

pre_commit/repository.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import shutil
55
from collections import defaultdict
66

7+
import pkg_resources
78
from cached_property import cached_property
89

910
from pre_commit import git
@@ -18,6 +19,10 @@
1819

1920
logger = logging.getLogger('pre_commit')
2021

22+
_pre_commit_version = pkg_resources.parse_version(
23+
pkg_resources.get_distribution('pre-commit').version
24+
)
25+
2126

2227
class Repository(object):
2328
def __init__(self, repo_config, repo_path_getter):
@@ -71,6 +76,18 @@ def hooks(self):
7176
)
7277
)
7378
exit(1)
79+
hook_version = pkg_resources.parse_version(
80+
self.manifest.hooks[hook['id']]['minimum_pre_commit_version'],
81+
)
82+
if hook_version > _pre_commit_version:
83+
logger.error(
84+
'The hook `{0}` requires pre-commit version {1} but '
85+
'version {2} is installed. '
86+
'Perhaps run `pip install --upgrade pre-commit`.'.format(
87+
hook['id'], hook_version, _pre_commit_version,
88+
)
89+
)
90+
exit(1)
7491
return tuple(
7592
(hook['id'], dict(self.manifest.hooks[hook['id']], **hook))
7693
for hook in self.repo_config['hooks']

testing/fixtures.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from __future__ import absolute_import
22
from __future__ import unicode_literals
33

4+
import contextlib
45
import io
56
import os.path
67

78
from aspy.yaml import ordered_dump
9+
from aspy.yaml import ordered_load
810

911
import pre_commit.constants as C
1012
from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA
@@ -35,6 +37,17 @@ def make_repo(tempdir_factory, repo_source):
3537
return path
3638

3739

40+
@contextlib.contextmanager
41+
def modify_manifest(path):
42+
"""Modify the manifest yielded by this context to write to hooks.yaml."""
43+
manifest_path = os.path.join(path, C.MANIFEST_FILE)
44+
manifest = ordered_load(io.open(manifest_path).read())
45+
yield manifest
46+
with io.open(manifest_path, 'w') as manifest_file:
47+
manifest_file.write(ordered_dump(manifest, **C.YAML_DUMP_KWARGS))
48+
cmd_output('git', 'commit', '-am', 'update hooks.yaml', cwd=path)
49+
50+
3851
def config_with_local_hooks():
3952
return OrderedDict((
4053
('repo', 'local'),

tests/manifest_test.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def test_manifest_contents(manifest):
2727
'id': 'bash_hook',
2828
'language': 'script',
2929
'language_version': 'default',
30+
'minimum_pre_commit_version': '0.0.0',
3031
'name': 'Bash hook',
3132
'stages': [],
3233
}]
@@ -42,6 +43,7 @@ def test_hooks(manifest):
4243
'id': 'bash_hook',
4344
'language': 'script',
4445
'language_version': 'default',
46+
'minimum_pre_commit_version': '0.0.0',
4547
'name': 'Bash hook',
4648
'stages': [],
4749
}

tests/repository_test.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
import logging
66
import os
77
import os.path
8+
import re
89
import shutil
910

1011
import mock
12+
import pkg_resources
1113
import pytest
1214

1315
from pre_commit import five
@@ -24,6 +26,7 @@
2426
from testing.fixtures import git_dir
2527
from testing.fixtures import make_config_from_repo
2628
from testing.fixtures import make_repo
29+
from testing.fixtures import modify_manifest
2730
from testing.util import skipif_slowtests_false
2831
from testing.util import xfailif_no_pcre_support
2932
from testing.util import xfailif_windows_no_node
@@ -525,3 +528,33 @@ def test_hook_id_not_present(tempdir_factory, store, fake_log_handler):
525528
'Typo? Perhaps it is introduced in a newer version? '
526529
'Often `pre-commit autoupdate` fixes this.'.format(path)
527530
)
531+
532+
533+
def test_too_new_version(tempdir_factory, store, fake_log_handler):
534+
path = make_repo(tempdir_factory, 'script_hooks_repo')
535+
with modify_manifest(path) as manifest:
536+
manifest[0]['minimum_pre_commit_version'] = '999.0.0'
537+
config = make_config_from_repo(path)
538+
repo = Repository.create(config, store)
539+
with pytest.raises(SystemExit):
540+
repo.install()
541+
msg = fake_log_handler.handle.call_args[0][0].msg
542+
assert re.match(
543+
r'^The hook `bash_hook` requires pre-commit version 999\.0\.0 but '
544+
r'version \d+\.\d+\.\d+ is installed. '
545+
r'Perhaps run `pip install --upgrade pre-commit`\.$',
546+
msg,
547+
)
548+
549+
550+
@pytest.mark.parametrize(
551+
'version',
552+
('0.1.0', pkg_resources.get_distribution('pre-commit').version),
553+
)
554+
def test_versions_ok(tempdir_factory, store, version):
555+
path = make_repo(tempdir_factory, 'script_hooks_repo')
556+
with modify_manifest(path) as manifest:
557+
manifest[0]['minimum_pre_commit_version'] = version
558+
config = make_config_from_repo(path)
559+
# Should succeed
560+
Repository.create(config, store).install()

0 commit comments

Comments
 (0)