We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
 Hooks should have a way of specifying minimum pre-commit version to run
Obviously this is a bit of a chicken-and-egg problem until the feature exists, but afterwards this'll be useful.

For instance, the feature added in 0.6.6 (additional dependencies) would be useful to gate against older pre-commit versions where installation would be nonsensical.

This would be a fatal error that would require the user to upgrade pre-commit to use that hook.

I'd like to implement this first, and then change all of the mirror repositories to use the additional-deps approach.  This would alleviate the pains in #282 as well by eliminating the hacky mirroring technique that is currently required for node hooks.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pre_commit/repository.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import logging
4 import shutil
5 from collections import defaultdict
6 
7 from cached_property import cached_property
8 
9 from pre_commit import git
10 from pre_commit.clientlib.validate_config import is_local_hooks
11 from pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA
12 from pre_commit.jsonschema_extensions import apply_defaults
13 from pre_commit.languages.all import languages
14 from pre_commit.languages.helpers import environment_dir
15 from pre_commit.manifest import Manifest
16 from pre_commit.prefixed_command_runner import PrefixedCommandRunner
17 
18 
19 logger = logging.getLogger('pre_commit')
20 
21 
22 class Repository(object):
23     def __init__(self, repo_config, repo_path_getter):
24         self.repo_config = repo_config
25         self.repo_path_getter = repo_path_getter
26         self.__installed = False
27 
28     @classmethod
29     def create(cls, config, store):
30         if is_local_hooks(config):
31             return LocalRepository(config)
32         else:
33             repo_path_getter = store.get_repo_path_getter(
34                 config['repo'], config['sha']
35             )
36             return cls(config, repo_path_getter)
37 
38     @cached_property
39     def repo_url(self):
40         return self.repo_config['repo']
41 
42     @cached_property
43     def sha(self):
44         return self.repo_config['sha']
45 
46     @cached_property
47     def languages(self):
48         return set(
49             (hook['language'], hook['language_version'])
50             for _, hook in self.hooks
51         )
52 
53     @cached_property
54     def additional_dependencies(self):
55         dep_dict = defaultdict(lambda: defaultdict(set))
56         for _, hook in self.hooks:
57             dep_dict[hook['language']][hook['language_version']].update(
58                 hook.get('additional_dependencies', []),
59             )
60         return dep_dict
61 
62     @cached_property
63     def hooks(self):
64         for hook in self.repo_config['hooks']:
65             if hook['id'] not in self.manifest.hooks:
66                 logger.error(
67                     '`{0}` is not present in repository {1}.  '
68                     'Typo? Perhaps it is introduced in a newer version?  '
69                     'Often `pre-commit autoupdate` fixes this.'.format(
70                         hook['id'], self.repo_config['repo'],
71                     )
72                 )
73                 exit(1)
74         return tuple(
75             (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))
76             for hook in self.repo_config['hooks']
77         )
78 
79     @cached_property
80     def manifest(self):
81         return Manifest(self.repo_path_getter)
82 
83     @cached_property
84     def cmd_runner(self):
85         return PrefixedCommandRunner(self.repo_path_getter.repo_path)
86 
87     def require_installed(self):
88         if self.__installed:
89             return
90 
91         self.install()
92         self.__installed = True
93 
94     def install(self):
95         """Install the hook repository."""
96         def language_is_installed(language_name, language_version):
97             language = languages[language_name]
98             directory = environment_dir(
99                 language.ENVIRONMENT_DIR, language_version,
100             )
101             return (
102                 directory is None or
103                 self.cmd_runner.exists(directory, '.installed')
104             )
105 
106         if not all(
107             language_is_installed(language_name, language_version)
108             for language_name, language_version in self.languages
109         ):
110             logger.info(
111                 'Installing environment for {0}.'.format(self.repo_url)
112             )
113             logger.info('Once installed this environment will be reused.')
114             logger.info('This may take a few minutes...')
115 
116         for language_name, language_version in self.languages:
117             language = languages[language_name]
118             if language_is_installed(language_name, language_version):
119                 continue
120 
121             directory = environment_dir(
122                 language.ENVIRONMENT_DIR, language_version,
123             )
124             # There's potentially incomplete cleanup from previous runs
125             # Clean it up!
126             if self.cmd_runner.exists(directory):
127                 shutil.rmtree(self.cmd_runner.path(directory))
128 
129             language.install_environment(
130                 self.cmd_runner, language_version,
131                 self.additional_dependencies[language_name][language_version],
132             )
133             # Touch the .installed file (atomic) to indicate we've installed
134             open(self.cmd_runner.path(directory, '.installed'), 'w').close()
135 
136     def run_hook(self, hook, file_args):
137         """Run a hook.
138 
139         Args:
140             hook - Hook dictionary
141             file_args - List of files to run
142         """
143         self.require_installed()
144         return languages[hook['language']].run_hook(
145             self.cmd_runner, hook, file_args,
146         )
147 
148 
149 class LocalRepository(Repository):
150     def __init__(self, repo_config):
151         super(LocalRepository, self).__init__(repo_config, None)
152 
153     @cached_property
154     def hooks(self):
155         return tuple(
156             (hook['id'], apply_defaults(hook, MANIFEST_JSON_SCHEMA['items']))
157             for hook in self.repo_config['hooks']
158         )
159 
160     @cached_property
161     def cmd_runner(self):
162         return PrefixedCommandRunner(git.get_root())
163 
164     @cached_property
165     def sha(self):
166         raise NotImplementedError
167 
168     @cached_property
169     def manifest(self):
170         raise NotImplementedError
171
```

Path: `pre_commit/clientlib/validate_manifest.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 from pre_commit.clientlib.validate_base import get_run_function
4 from pre_commit.clientlib.validate_base import get_validator
5 from pre_commit.clientlib.validate_base import is_regex_valid
6 from pre_commit.languages.all import all_languages
7 
8 
9 class InvalidManifestError(ValueError):
10     pass
11 
12 
13 MANIFEST_JSON_SCHEMA = {
14     'type': 'array',
15     'minItems': 1,
16     'items': {
17         'type': 'object',
18         'properties': {
19             'id': {'type': 'string'},
20             'name': {'type': 'string'},
21             'description': {'type': 'string', 'default': ''},
22             'entry': {'type': 'string'},
23             'exclude': {'type': 'string', 'default': '^$'},
24             'language': {'type': 'string'},
25             'language_version': {'type': 'string', 'default': 'default'},
26             'files': {'type': 'string'},
27             'stages': {
28                 'type': 'array',
29                 'default': [],
30                 'items': {
31                     'type': 'string',
32                 },
33             },
34             'args': {
35                 'type': 'array',
36                 'default': [],
37                 'items': {
38                     'type': 'string',
39                 },
40             },
41             'additional_dependencies': {
42                 'type': 'array',
43                 'items': {'type': 'string'},
44             },
45         },
46         'required': ['id', 'name', 'entry', 'language', 'files'],
47     },
48 }
49 
50 
51 def validate_languages(hook_config):
52     if hook_config['language'] not in all_languages:
53         raise InvalidManifestError(
54             'Expected language {0} for {1} to be one of {2!r}'.format(
55                 hook_config['id'],
56                 hook_config['language'],
57                 all_languages,
58             )
59         )
60 
61 
62 def validate_files(hook_config):
63     if not is_regex_valid(hook_config['files']):
64         raise InvalidManifestError(
65             'Invalid files regex at {0}: {1}'.format(
66                 hook_config['id'], hook_config['files'],
67             )
68         )
69 
70     if not is_regex_valid(hook_config.get('exclude', '')):
71         raise InvalidManifestError(
72             'Invalid exclude regex at {0}: {1}'.format(
73                 hook_config['id'], hook_config['exclude'],
74             )
75         )
76 
77 
78 def additional_manifest_check(obj):
79     for hook_config in obj:
80         validate_languages(hook_config)
81         validate_files(hook_config)
82 
83 
84 load_manifest = get_validator(
85     MANIFEST_JSON_SCHEMA,
86     InvalidManifestError,
87     additional_manifest_check,
88 )
89 
90 
91 run = get_run_function(
92     'Manifest filenames.',
93     load_manifest,
94     InvalidManifestError,
95 )
96 
97 
98 if __name__ == '__main__':
99     exit(run())
100
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pre_commit/clientlib/validate_manifest.py b/pre_commit/clientlib/validate_manifest.py
--- a/pre_commit/clientlib/validate_manifest.py
+++ b/pre_commit/clientlib/validate_manifest.py
@@ -23,6 +23,9 @@
             'exclude': {'type': 'string', 'default': '^$'},
             'language': {'type': 'string'},
             'language_version': {'type': 'string', 'default': 'default'},
+            'minimum_pre_commit_version': {
+                'type': 'string', 'default': '0.0.0',
+            },
             'files': {'type': 'string'},
             'stages': {
                 'type': 'array',
diff --git a/pre_commit/repository.py b/pre_commit/repository.py
--- a/pre_commit/repository.py
+++ b/pre_commit/repository.py
@@ -4,6 +4,7 @@
 import shutil
 from collections import defaultdict
 
+import pkg_resources
 from cached_property import cached_property
 
 from pre_commit import git
@@ -18,6 +19,10 @@
 
 logger = logging.getLogger('pre_commit')
 
+_pre_commit_version = pkg_resources.parse_version(
+    pkg_resources.get_distribution('pre-commit').version
+)
+
 
 class Repository(object):
     def __init__(self, repo_config, repo_path_getter):
@@ -71,6 +76,18 @@
                     )
                 )
                 exit(1)
+            hook_version = pkg_resources.parse_version(
+                self.manifest.hooks[hook['id']]['minimum_pre_commit_version'],
+            )
+            if hook_version > _pre_commit_version:
+                logger.error(
+                    'The hook `{0}` requires pre-commit version {1} but '
+                    'version {2} is installed.  '
+                    'Perhaps run `pip install --upgrade pre-commit`.'.format(
+                        hook['id'], hook_version, _pre_commit_version,
+                    )
+                )
+                exit(1)
         return tuple(
             (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))
             for hook in self.repo_config['hooks']

{"golden_diff": "diff --git a/pre_commit/clientlib/validate_manifest.py b/pre_commit/clientlib/validate_manifest.py\n--- a/pre_commit/clientlib/validate_manifest.py\n+++ b/pre_commit/clientlib/validate_manifest.py\n@@ -23,6 +23,9 @@\n             'exclude': {'type': 'string', 'default': '^$'},\n             'language': {'type': 'string'},\n             'language_version': {'type': 'string', 'default': 'default'},\n+            'minimum_pre_commit_version': {\n+                'type': 'string', 'default': '0.0.0',\n+            },\n             'files': {'type': 'string'},\n             'stages': {\n                 'type': 'array',\ndiff --git a/pre_commit/repository.py b/pre_commit/repository.py\n--- a/pre_commit/repository.py\n+++ b/pre_commit/repository.py\n@@ -4,6 +4,7 @@\n import shutil\n from collections import defaultdict\n \n+import pkg_resources\n from cached_property import cached_property\n \n from pre_commit import git\n@@ -18,6 +19,10 @@\n \n logger = logging.getLogger('pre_commit')\n \n+_pre_commit_version = pkg_resources.parse_version(\n+    pkg_resources.get_distribution('pre-commit').version\n+)\n+\n \n class Repository(object):\n     def __init__(self, repo_config, repo_path_getter):\n@@ -71,6 +76,18 @@\n                     )\n                 )\n                 exit(1)\n+            hook_version = pkg_resources.parse_version(\n+                self.manifest.hooks[hook['id']]['minimum_pre_commit_version'],\n+            )\n+            if hook_version > _pre_commit_version:\n+                logger.error(\n+                    'The hook `{0}` requires pre-commit version {1} but '\n+                    'version {2} is installed.  '\n+                    'Perhaps run `pip install --upgrade pre-commit`.'.format(\n+                        hook['id'], hook_version, _pre_commit_version,\n+                    )\n+                )\n+                exit(1)\n         return tuple(\n             (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))\n             for hook in self.repo_config['hooks']\n", "issue": " Hooks should have a way of specifying minimum pre-commit version to run\nObviously this is a bit of a chicken-and-egg problem until the feature exists, but afterwards this'll be useful.\n\nFor instance, the feature added in 0.6.6 (additional dependencies) would be useful to gate against older pre-commit versions where installation would be nonsensical.\n\nThis would be a fatal error that would require the user to upgrade pre-commit to use that hook.\n\nI'd like to implement this first, and then change all of the mirror repositories to use the additional-deps approach.  This would alleviate the pains in #282 as well by eliminating the hacky mirroring technique that is currently required for node hooks.\n\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport shutil\nfrom collections import defaultdict\n\nfrom cached_property import cached_property\n\nfrom pre_commit import git\nfrom pre_commit.clientlib.validate_config import is_local_hooks\nfrom pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA\nfrom pre_commit.jsonschema_extensions import apply_defaults\nfrom pre_commit.languages.all import languages\nfrom pre_commit.languages.helpers import environment_dir\nfrom pre_commit.manifest import Manifest\nfrom pre_commit.prefixed_command_runner import PrefixedCommandRunner\n\n\nlogger = logging.getLogger('pre_commit')\n\n\nclass Repository(object):\n    def __init__(self, repo_config, repo_path_getter):\n        self.repo_config = repo_config\n        self.repo_path_getter = repo_path_getter\n        self.__installed = False\n\n    @classmethod\n    def create(cls, config, store):\n        if is_local_hooks(config):\n            return LocalRepository(config)\n        else:\n            repo_path_getter = store.get_repo_path_getter(\n                config['repo'], config['sha']\n            )\n            return cls(config, repo_path_getter)\n\n    @cached_property\n    def repo_url(self):\n        return self.repo_config['repo']\n\n    @cached_property\n    def sha(self):\n        return self.repo_config['sha']\n\n    @cached_property\n    def languages(self):\n        return set(\n            (hook['language'], hook['language_version'])\n            for _, hook in self.hooks\n        )\n\n    @cached_property\n    def additional_dependencies(self):\n        dep_dict = defaultdict(lambda: defaultdict(set))\n        for _, hook in self.hooks:\n            dep_dict[hook['language']][hook['language_version']].update(\n                hook.get('additional_dependencies', []),\n            )\n        return dep_dict\n\n    @cached_property\n    def hooks(self):\n        for hook in self.repo_config['hooks']:\n            if hook['id'] not in self.manifest.hooks:\n                logger.error(\n                    '`{0}` is not present in repository {1}.  '\n                    'Typo? Perhaps it is introduced in a newer version?  '\n                    'Often `pre-commit autoupdate` fixes this.'.format(\n                        hook['id'], self.repo_config['repo'],\n                    )\n                )\n                exit(1)\n        return tuple(\n            (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def manifest(self):\n        return Manifest(self.repo_path_getter)\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(self.repo_path_getter.repo_path)\n\n    def require_installed(self):\n        if self.__installed:\n            return\n\n        self.install()\n        self.__installed = True\n\n    def install(self):\n        \"\"\"Install the hook repository.\"\"\"\n        def language_is_installed(language_name, language_version):\n            language = languages[language_name]\n            directory = environment_dir(\n                language.ENVIRONMENT_DIR, language_version,\n            )\n            return (\n                directory is None or\n                self.cmd_runner.exists(directory, '.installed')\n            )\n\n        if not all(\n            language_is_installed(language_name, language_version)\n            for language_name, language_version in self.languages\n        ):\n            logger.info(\n                'Installing environment for {0}.'.format(self.repo_url)\n            )\n            logger.info('Once installed this environment will be reused.')\n            logger.info('This may take a few minutes...')\n\n        for language_name, language_version in self.languages:\n            language = languages[language_name]\n            if language_is_installed(language_name, language_version):\n                continue\n\n            directory = environment_dir(\n                language.ENVIRONMENT_DIR, language_version,\n            )\n            # There's potentially incomplete cleanup from previous runs\n            # Clean it up!\n            if self.cmd_runner.exists(directory):\n                shutil.rmtree(self.cmd_runner.path(directory))\n\n            language.install_environment(\n                self.cmd_runner, language_version,\n                self.additional_dependencies[language_name][language_version],\n            )\n            # Touch the .installed file (atomic) to indicate we've installed\n            open(self.cmd_runner.path(directory, '.installed'), 'w').close()\n\n    def run_hook(self, hook, file_args):\n        \"\"\"Run a hook.\n\n        Args:\n            hook - Hook dictionary\n            file_args - List of files to run\n        \"\"\"\n        self.require_installed()\n        return languages[hook['language']].run_hook(\n            self.cmd_runner, hook, file_args,\n        )\n\n\nclass LocalRepository(Repository):\n    def __init__(self, repo_config):\n        super(LocalRepository, self).__init__(repo_config, None)\n\n    @cached_property\n    def hooks(self):\n        return tuple(\n            (hook['id'], apply_defaults(hook, MANIFEST_JSON_SCHEMA['items']))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(git.get_root())\n\n    @cached_property\n    def sha(self):\n        raise NotImplementedError\n\n    @cached_property\n    def manifest(self):\n        raise NotImplementedError\n", "path": "pre_commit/repository.py"}, {"content": "from __future__ import unicode_literals\n\nfrom pre_commit.clientlib.validate_base import get_run_function\nfrom pre_commit.clientlib.validate_base import get_validator\nfrom pre_commit.clientlib.validate_base import is_regex_valid\nfrom pre_commit.languages.all import all_languages\n\n\nclass InvalidManifestError(ValueError):\n    pass\n\n\nMANIFEST_JSON_SCHEMA = {\n    'type': 'array',\n    'minItems': 1,\n    'items': {\n        'type': 'object',\n        'properties': {\n            'id': {'type': 'string'},\n            'name': {'type': 'string'},\n            'description': {'type': 'string', 'default': ''},\n            'entry': {'type': 'string'},\n            'exclude': {'type': 'string', 'default': '^$'},\n            'language': {'type': 'string'},\n            'language_version': {'type': 'string', 'default': 'default'},\n            'files': {'type': 'string'},\n            'stages': {\n                'type': 'array',\n                'default': [],\n                'items': {\n                    'type': 'string',\n                },\n            },\n            'args': {\n                'type': 'array',\n                'default': [],\n                'items': {\n                    'type': 'string',\n                },\n            },\n            'additional_dependencies': {\n                'type': 'array',\n                'items': {'type': 'string'},\n            },\n        },\n        'required': ['id', 'name', 'entry', 'language', 'files'],\n    },\n}\n\n\ndef validate_languages(hook_config):\n    if hook_config['language'] not in all_languages:\n        raise InvalidManifestError(\n            'Expected language {0} for {1} to be one of {2!r}'.format(\n                hook_config['id'],\n                hook_config['language'],\n                all_languages,\n            )\n        )\n\n\ndef validate_files(hook_config):\n    if not is_regex_valid(hook_config['files']):\n        raise InvalidManifestError(\n            'Invalid files regex at {0}: {1}'.format(\n                hook_config['id'], hook_config['files'],\n            )\n        )\n\n    if not is_regex_valid(hook_config.get('exclude', '')):\n        raise InvalidManifestError(\n            'Invalid exclude regex at {0}: {1}'.format(\n                hook_config['id'], hook_config['exclude'],\n            )\n        )\n\n\ndef additional_manifest_check(obj):\n    for hook_config in obj:\n        validate_languages(hook_config)\n        validate_files(hook_config)\n\n\nload_manifest = get_validator(\n    MANIFEST_JSON_SCHEMA,\n    InvalidManifestError,\n    additional_manifest_check,\n)\n\n\nrun = get_run_function(\n    'Manifest filenames.',\n    load_manifest,\n    InvalidManifestError,\n)\n\n\nif __name__ == '__main__':\n    exit(run())\n", "path": "pre_commit/clientlib/validate_manifest.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport shutil\nfrom collections import defaultdict\n\nimport pkg_resources\nfrom cached_property import cached_property\n\nfrom pre_commit import git\nfrom pre_commit.clientlib.validate_config import is_local_hooks\nfrom pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA\nfrom pre_commit.jsonschema_extensions import apply_defaults\nfrom pre_commit.languages.all import languages\nfrom pre_commit.languages.helpers import environment_dir\nfrom pre_commit.manifest import Manifest\nfrom pre_commit.prefixed_command_runner import PrefixedCommandRunner\n\n\nlogger = logging.getLogger('pre_commit')\n\n_pre_commit_version = pkg_resources.parse_version(\n    pkg_resources.get_distribution('pre-commit').version\n)\n\n\nclass Repository(object):\n    def __init__(self, repo_config, repo_path_getter):\n        self.repo_config = repo_config\n        self.repo_path_getter = repo_path_getter\n        self.__installed = False\n\n    @classmethod\n    def create(cls, config, store):\n        if is_local_hooks(config):\n            return LocalRepository(config)\n        else:\n            repo_path_getter = store.get_repo_path_getter(\n                config['repo'], config['sha']\n            )\n            return cls(config, repo_path_getter)\n\n    @cached_property\n    def repo_url(self):\n        return self.repo_config['repo']\n\n    @cached_property\n    def sha(self):\n        return self.repo_config['sha']\n\n    @cached_property\n    def languages(self):\n        return set(\n            (hook['language'], hook['language_version'])\n            for _, hook in self.hooks\n        )\n\n    @cached_property\n    def additional_dependencies(self):\n        dep_dict = defaultdict(lambda: defaultdict(set))\n        for _, hook in self.hooks:\n            dep_dict[hook['language']][hook['language_version']].update(\n                hook.get('additional_dependencies', []),\n            )\n        return dep_dict\n\n    @cached_property\n    def hooks(self):\n        for hook in self.repo_config['hooks']:\n            if hook['id'] not in self.manifest.hooks:\n                logger.error(\n                    '`{0}` is not present in repository {1}.  '\n                    'Typo? Perhaps it is introduced in a newer version?  '\n                    'Often `pre-commit autoupdate` fixes this.'.format(\n                        hook['id'], self.repo_config['repo'],\n                    )\n                )\n                exit(1)\n            hook_version = pkg_resources.parse_version(\n                self.manifest.hooks[hook['id']]['minimum_pre_commit_version'],\n            )\n            if hook_version > _pre_commit_version:\n                logger.error(\n                    'The hook `{0}` requires pre-commit version {1} but '\n                    'version {2} is installed.  '\n                    'Perhaps run `pip install --upgrade pre-commit`.'.format(\n                        hook['id'], hook_version, _pre_commit_version,\n                    )\n                )\n                exit(1)\n        return tuple(\n            (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def manifest(self):\n        return Manifest(self.repo_path_getter)\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(self.repo_path_getter.repo_path)\n\n    def require_installed(self):\n        if self.__installed:\n            return\n\n        self.install()\n        self.__installed = True\n\n    def install(self):\n        \"\"\"Install the hook repository.\"\"\"\n        def language_is_installed(language_name, language_version):\n            language = languages[language_name]\n            directory = environment_dir(\n                language.ENVIRONMENT_DIR, language_version,\n            )\n            return (\n                directory is None or\n                self.cmd_runner.exists(directory, '.installed')\n            )\n\n        if not all(\n            language_is_installed(language_name, language_version)\n            for language_name, language_version in self.languages\n        ):\n            logger.info(\n                'Installing environment for {0}.'.format(self.repo_url)\n            )\n            logger.info('Once installed this environment will be reused.')\n            logger.info('This may take a few minutes...')\n\n        for language_name, language_version in self.languages:\n            language = languages[language_name]\n            if language_is_installed(language_name, language_version):\n                continue\n\n            directory = environment_dir(\n                language.ENVIRONMENT_DIR, language_version,\n            )\n            # There's potentially incomplete cleanup from previous runs\n            # Clean it up!\n            if self.cmd_runner.exists(directory):\n                shutil.rmtree(self.cmd_runner.path(directory))\n\n            language.install_environment(\n                self.cmd_runner, language_version,\n                self.additional_dependencies[language_name][language_version],\n            )\n            # Touch the .installed file (atomic) to indicate we've installed\n            open(self.cmd_runner.path(directory, '.installed'), 'w').close()\n\n    def run_hook(self, hook, file_args):\n        \"\"\"Run a hook.\n\n        Args:\n            hook - Hook dictionary\n            file_args - List of files to run\n        \"\"\"\n        self.require_installed()\n        return languages[hook['language']].run_hook(\n            self.cmd_runner, hook, file_args,\n        )\n\n\nclass LocalRepository(Repository):\n    def __init__(self, repo_config):\n        super(LocalRepository, self).__init__(repo_config, None)\n\n    @cached_property\n    def hooks(self):\n        return tuple(\n            (hook['id'], apply_defaults(hook, MANIFEST_JSON_SCHEMA['items']))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(git.get_root())\n\n    @cached_property\n    def sha(self):\n        raise NotImplementedError\n\n    @cached_property\n    def manifest(self):\n        raise NotImplementedError\n", "path": "pre_commit/repository.py"}, {"content": "from __future__ import unicode_literals\n\nfrom pre_commit.clientlib.validate_base import get_run_function\nfrom pre_commit.clientlib.validate_base import get_validator\nfrom pre_commit.clientlib.validate_base import is_regex_valid\nfrom pre_commit.languages.all import all_languages\n\n\nclass InvalidManifestError(ValueError):\n    pass\n\n\nMANIFEST_JSON_SCHEMA = {\n    'type': 'array',\n    'minItems': 1,\n    'items': {\n        'type': 'object',\n        'properties': {\n            'id': {'type': 'string'},\n            'name': {'type': 'string'},\n            'description': {'type': 'string', 'default': ''},\n            'entry': {'type': 'string'},\n            'exclude': {'type': 'string', 'default': '^$'},\n            'language': {'type': 'string'},\n            'language_version': {'type': 'string', 'default': 'default'},\n            'minimum_pre_commit_version': {\n                'type': 'string', 'default': '0.0.0',\n            },\n            'files': {'type': 'string'},\n            'stages': {\n                'type': 'array',\n                'default': [],\n                'items': {\n                    'type': 'string',\n                },\n            },\n            'args': {\n                'type': 'array',\n                'default': [],\n                'items': {\n                    'type': 'string',\n                },\n            },\n            'additional_dependencies': {\n                'type': 'array',\n                'items': {'type': 'string'},\n            },\n        },\n        'required': ['id', 'name', 'entry', 'language', 'files'],\n    },\n}\n\n\ndef validate_languages(hook_config):\n    if hook_config['language'] not in all_languages:\n        raise InvalidManifestError(\n            'Expected language {0} for {1} to be one of {2!r}'.format(\n                hook_config['id'],\n                hook_config['language'],\n                all_languages,\n            )\n        )\n\n\ndef validate_files(hook_config):\n    if not is_regex_valid(hook_config['files']):\n        raise InvalidManifestError(\n            'Invalid files regex at {0}: {1}'.format(\n                hook_config['id'], hook_config['files'],\n            )\n        )\n\n    if not is_regex_valid(hook_config.get('exclude', '')):\n        raise InvalidManifestError(\n            'Invalid exclude regex at {0}: {1}'.format(\n                hook_config['id'], hook_config['exclude'],\n            )\n        )\n\n\ndef additional_manifest_check(obj):\n    for hook_config in obj:\n        validate_languages(hook_config)\n        validate_files(hook_config)\n\n\nload_manifest = get_validator(\n    MANIFEST_JSON_SCHEMA,\n    InvalidManifestError,\n    additional_manifest_check,\n)\n\n\nrun = get_run_function(\n    'Manifest filenames.',\n    load_manifest,\n    InvalidManifestError,\n)\n\n\nif __name__ == '__main__':\n    exit(run())\n", "path": "pre_commit/clientlib/validate_manifest.py"}]}