mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-02-19 09:04:41 +04:00
Merge pull request #295 from blarghmatey/install_dependencies
Added the additional_dependencies config parameter
This commit is contained in:
commit
7c8272da77
11 changed files with 132 additions and 17 deletions
|
|
@ -39,6 +39,10 @@ CONFIG_JSON_SCHEMA = {
|
||||||
'type': 'array',
|
'type': 'array',
|
||||||
'items': {'type': 'string'},
|
'items': {'type': 'string'},
|
||||||
},
|
},
|
||||||
|
'additional_dependencies': {
|
||||||
|
'type': 'array',
|
||||||
|
'items': {'type': 'string'}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'required': ['id'],
|
'required': ['id'],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,11 @@ from pre_commit.languages import system
|
||||||
# # Use None for no environment
|
# # Use None for no environment
|
||||||
# ENVIRONMENT_DIR = 'foo_env'
|
# ENVIRONMENT_DIR = 'foo_env'
|
||||||
#
|
#
|
||||||
# def install_environment(repo_cmd_runner, version='default'):
|
# def install_environment(
|
||||||
|
# repo_cmd_runner,
|
||||||
|
# version='default',
|
||||||
|
# additional_dependencies=None,
|
||||||
|
# ):
|
||||||
# """Installs a repository in the given repository. Note that the current
|
# """Installs a repository in the given repository. Note that the current
|
||||||
# working directory will already be inside the repository.
|
# working directory will already be inside the repository.
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import sys
|
||||||
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.util import clean_path_on_failure
|
from pre_commit.util import clean_path_on_failure
|
||||||
|
from pre_commit.util import shell_escape
|
||||||
|
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'node_env'
|
ENVIRONMENT_DIR = 'node_env'
|
||||||
|
|
@ -23,7 +24,11 @@ def in_env(repo_cmd_runner, language_version):
|
||||||
yield NodeEnv(repo_cmd_runner, language_version)
|
yield NodeEnv(repo_cmd_runner, language_version)
|
||||||
|
|
||||||
|
|
||||||
def install_environment(repo_cmd_runner, version='default'):
|
def install_environment(
|
||||||
|
repo_cmd_runner,
|
||||||
|
version='default',
|
||||||
|
additional_dependencies=None,
|
||||||
|
):
|
||||||
assert repo_cmd_runner.exists('package.json')
|
assert repo_cmd_runner.exists('package.json')
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
||||||
|
|
||||||
|
|
@ -41,6 +46,13 @@ def install_environment(repo_cmd_runner, version='default'):
|
||||||
|
|
||||||
with in_env(repo_cmd_runner, version) as node_env:
|
with in_env(repo_cmd_runner, version) as node_env:
|
||||||
node_env.run("cd '{prefix}' && npm install -g")
|
node_env.run("cd '{prefix}' && npm install -g")
|
||||||
|
if additional_dependencies:
|
||||||
|
node_env.run(
|
||||||
|
"cd '{prefix}' && npm install -g " +
|
||||||
|
' '.join(
|
||||||
|
shell_escape(dep) for dep in additional_dependencies
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(repo_cmd_runner, hook, file_args):
|
def run_hook(repo_cmd_runner, hook, file_args):
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,11 @@ from pre_commit.util import shell_escape
|
||||||
ENVIRONMENT_DIR = None
|
ENVIRONMENT_DIR = None
|
||||||
|
|
||||||
|
|
||||||
def install_environment(repo_cmd_runner, version='default'):
|
def install_environment(
|
||||||
|
repo_cmd_runner,
|
||||||
|
version='default',
|
||||||
|
additional_dependencies=None,
|
||||||
|
):
|
||||||
"""Installation for pcre type is a noop."""
|
"""Installation for pcre type is a noop."""
|
||||||
raise AssertionError('Cannot install pcre repo.')
|
raise AssertionError('Cannot install pcre repo.')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import virtualenv
|
||||||
|
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.util import clean_path_on_failure
|
from pre_commit.util import clean_path_on_failure
|
||||||
|
from pre_commit.util import shell_escape
|
||||||
|
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'py_env'
|
ENVIRONMENT_DIR = 'py_env'
|
||||||
|
|
@ -42,7 +43,11 @@ def norm_version(version):
|
||||||
return version
|
return version
|
||||||
|
|
||||||
|
|
||||||
def install_environment(repo_cmd_runner, version='default'):
|
def install_environment(
|
||||||
|
repo_cmd_runner,
|
||||||
|
version='default',
|
||||||
|
additional_dependencies=None,
|
||||||
|
):
|
||||||
assert repo_cmd_runner.exists('setup.py')
|
assert repo_cmd_runner.exists('setup.py')
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
||||||
|
|
||||||
|
|
@ -57,6 +62,13 @@ def install_environment(repo_cmd_runner, version='default'):
|
||||||
repo_cmd_runner.run(venv_cmd)
|
repo_cmd_runner.run(venv_cmd)
|
||||||
with in_env(repo_cmd_runner, version) as env:
|
with in_env(repo_cmd_runner, version) as env:
|
||||||
env.run("cd '{prefix}' && pip install .")
|
env.run("cd '{prefix}' && pip install .")
|
||||||
|
if additional_dependencies:
|
||||||
|
env.run(
|
||||||
|
"cd '{prefix}' && pip install " +
|
||||||
|
' '.join(
|
||||||
|
shell_escape(dep) for dep in additional_dependencies
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(repo_cmd_runner, hook, file_args):
|
def run_hook(repo_cmd_runner, hook, file_args):
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from pre_commit.languages import helpers
|
||||||
from pre_commit.util import CalledProcessError
|
from pre_commit.util import CalledProcessError
|
||||||
from pre_commit.util import clean_path_on_failure
|
from pre_commit.util import clean_path_on_failure
|
||||||
from pre_commit.util import resource_filename
|
from pre_commit.util import resource_filename
|
||||||
|
from pre_commit.util import shell_escape
|
||||||
from pre_commit.util import tarfile_open
|
from pre_commit.util import tarfile_open
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -78,7 +79,11 @@ def _install_ruby(environment, version):
|
||||||
environment.run('rbenv install {0}'.format(version))
|
environment.run('rbenv install {0}'.format(version))
|
||||||
|
|
||||||
|
|
||||||
def install_environment(repo_cmd_runner, version='default'):
|
def install_environment(
|
||||||
|
repo_cmd_runner,
|
||||||
|
version='default',
|
||||||
|
additional_dependencies=None,
|
||||||
|
):
|
||||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
||||||
with clean_path_on_failure(repo_cmd_runner.path(directory)):
|
with clean_path_on_failure(repo_cmd_runner.path(directory)):
|
||||||
# TODO: this currently will fail if there's no version specified and
|
# TODO: this currently will fail if there's no version specified and
|
||||||
|
|
@ -88,9 +93,16 @@ def install_environment(repo_cmd_runner, version='default'):
|
||||||
if version != 'default':
|
if version != 'default':
|
||||||
_install_ruby(ruby_env, version)
|
_install_ruby(ruby_env, version)
|
||||||
ruby_env.run(
|
ruby_env.run(
|
||||||
'cd {prefix} && gem build *.gemspec'
|
'cd {prefix} && gem build *.gemspec && '
|
||||||
' && gem install --no-ri --no-rdoc *.gem',
|
'gem install --no-ri --no-rdoc *.gem',
|
||||||
)
|
)
|
||||||
|
if additional_dependencies:
|
||||||
|
ruby_env.run(
|
||||||
|
'cd {prefix} && gem install --no-ri --no-rdoc ' +
|
||||||
|
' '.join(
|
||||||
|
shell_escape(dep) for dep in additional_dependencies
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_hook(repo_cmd_runner, hook, file_args):
|
def run_hook(repo_cmd_runner, hook, file_args):
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,11 @@ from pre_commit.languages.helpers import file_args_to_stdin
|
||||||
ENVIRONMENT_DIR = None
|
ENVIRONMENT_DIR = None
|
||||||
|
|
||||||
|
|
||||||
def install_environment(repo_cmd_runner, version='default'):
|
def install_environment(
|
||||||
|
repo_cmd_runner,
|
||||||
|
version='default',
|
||||||
|
additional_dependencies=None,
|
||||||
|
):
|
||||||
"""Installation for script type is a noop."""
|
"""Installation for script type is a noop."""
|
||||||
raise AssertionError('Cannot install script repo.')
|
raise AssertionError('Cannot install script repo.')
|
||||||
|
|
||||||
|
|
@ -14,7 +18,6 @@ def install_environment(repo_cmd_runner, version='default'):
|
||||||
def run_hook(repo_cmd_runner, hook, file_args):
|
def run_hook(repo_cmd_runner, hook, file_args):
|
||||||
return repo_cmd_runner.run(
|
return repo_cmd_runner.run(
|
||||||
['xargs', '-0', '{{prefix}}{0}'.format(hook['entry'])] + hook['args'],
|
['xargs', '-0', '{{prefix}}{0}'.format(hook['entry'])] + hook['args'],
|
||||||
# TODO: this is duplicated in pre_commit/languages/helpers.py
|
|
||||||
stdin=file_args_to_stdin(file_args),
|
stdin=file_args_to_stdin(file_args),
|
||||||
retcode=None,
|
retcode=None,
|
||||||
encoding=None,
|
encoding=None,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,11 @@ from pre_commit.languages.helpers import file_args_to_stdin
|
||||||
ENVIRONMENT_DIR = None
|
ENVIRONMENT_DIR = None
|
||||||
|
|
||||||
|
|
||||||
def install_environment(repo_cmd_runner, version='default'):
|
def install_environment(
|
||||||
|
repo_cmd_runner,
|
||||||
|
version='default',
|
||||||
|
additional_dependencies=None,
|
||||||
|
):
|
||||||
"""Installation for system type is a noop."""
|
"""Installation for system type is a noop."""
|
||||||
raise AssertionError('Cannot install system repo.')
|
raise AssertionError('Cannot install system repo.')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import shutil
|
import shutil
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from cached_property import cached_property
|
from cached_property import cached_property
|
||||||
|
|
||||||
|
|
@ -49,6 +50,14 @@ class Repository(object):
|
||||||
for _, hook in self.hooks
|
for _, hook in self.hooks
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def additional_dependencies(self):
|
||||||
|
dep_dict = defaultdict(lambda: defaultdict(set))
|
||||||
|
for _, hook in self.hooks:
|
||||||
|
dep_dict[hook['language']][hook['language_version']].update(
|
||||||
|
hook.get('additional_dependencies', []))
|
||||||
|
return dep_dict
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def hooks(self):
|
def hooks(self):
|
||||||
# TODO: merging in manifest dicts is a smell imo
|
# TODO: merging in manifest dicts is a smell imo
|
||||||
|
|
@ -107,7 +116,9 @@ class Repository(object):
|
||||||
if self.cmd_runner.exists(directory):
|
if self.cmd_runner.exists(directory):
|
||||||
shutil.rmtree(self.cmd_runner.path(directory))
|
shutil.rmtree(self.cmd_runner.path(directory))
|
||||||
|
|
||||||
language.install_environment(self.cmd_runner, language_version)
|
language.install_environment(
|
||||||
|
self.cmd_runner, language_version,
|
||||||
|
self.additional_dependencies[language_name][language_version])
|
||||||
# Touch the .installed file (atomic) to indicate we've installed
|
# Touch the .installed file (atomic) to indicate we've installed
|
||||||
open(self.cmd_runner.path(directory, '.installed'), 'w').close()
|
open(self.cmd_runner.path(directory, '.installed'), 'w').close()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ from pre_commit.languages.all import languages
|
||||||
@pytest.mark.parametrize('language', all_languages)
|
@pytest.mark.parametrize('language', all_languages)
|
||||||
def test_install_environment_argspec(language):
|
def test_install_environment_argspec(language):
|
||||||
expected_argspec = inspect.ArgSpec(
|
expected_argspec = inspect.ArgSpec(
|
||||||
args=['repo_cmd_runner', 'version'],
|
args=['repo_cmd_runner', 'version', 'additional_dependencies'],
|
||||||
varargs=None,
|
varargs=None,
|
||||||
keywords=None,
|
keywords=None,
|
||||||
defaults=('default',),
|
defaults=('default', None),
|
||||||
)
|
)
|
||||||
argspec = inspect.getargspec(languages[language].install_environment)
|
argspec = inspect.getargspec(languages[language].install_environment)
|
||||||
assert argspec == expected_argspec
|
assert argspec == expected_argspec
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,9 @@ from pre_commit import five
|
||||||
from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA
|
from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA
|
||||||
from pre_commit.clientlib.validate_config import validate_config_extra
|
from pre_commit.clientlib.validate_config import validate_config_extra
|
||||||
from pre_commit.jsonschema_extensions import apply_defaults
|
from pre_commit.jsonschema_extensions import apply_defaults
|
||||||
from pre_commit.languages.python import norm_version
|
from pre_commit.languages import node
|
||||||
from pre_commit.languages.python import PythonEnv
|
from pre_commit.languages import python
|
||||||
|
from pre_commit.languages import ruby
|
||||||
from pre_commit.repository import Repository
|
from pre_commit.repository import Repository
|
||||||
from pre_commit.util import cmd_output
|
from pre_commit.util import cmd_output
|
||||||
from pre_commit.util import cwd
|
from pre_commit.util import cwd
|
||||||
|
|
@ -293,6 +294,7 @@ def mock_repo_config():
|
||||||
'hooks': [{
|
'hooks': [{
|
||||||
'id': 'pyflakes',
|
'id': 'pyflakes',
|
||||||
'files': '\\.py$',
|
'files': '\\.py$',
|
||||||
|
'additional_dependencies': ['pep8']
|
||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
config_wrapped = apply_defaults([config], CONFIG_JSON_SCHEMA)
|
config_wrapped = apply_defaults([config], CONFIG_JSON_SCHEMA)
|
||||||
|
|
@ -318,6 +320,53 @@ def test_languages(tempdir_factory, store):
|
||||||
assert repo.languages == set([('python', 'default')])
|
assert repo.languages == set([('python', 'default')])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
def test_additional_dependencies(tempdir_factory, store):
|
||||||
|
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
||||||
|
config = make_config_from_repo(path)
|
||||||
|
config['hooks'][0]['additional_dependencies'] = ['pep8']
|
||||||
|
repo = Repository.create(config, store)
|
||||||
|
assert repo.additional_dependencies['python']['default'] == set(('pep8',))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
def test_additional_python_dependencies_installed(tempdir_factory, store):
|
||||||
|
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
||||||
|
config = make_config_from_repo(path)
|
||||||
|
config['hooks'][0]['additional_dependencies'] = ['mccabe']
|
||||||
|
repo = Repository.create(config, store)
|
||||||
|
repo.run_hook(repo.hooks[0][1], [])
|
||||||
|
with python.in_env(repo.cmd_runner, 'default') as env:
|
||||||
|
output = env.run('pip freeze -l')[1]
|
||||||
|
assert 'mccabe' in output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
def test_additional_ruby_dependencies_installed(tempdir_factory, store):
|
||||||
|
path = make_repo(tempdir_factory, 'ruby_hooks_repo')
|
||||||
|
config = make_config_from_repo(path)
|
||||||
|
config['hooks'][0]['additional_dependencies'] = ['mime-types']
|
||||||
|
repo = Repository.create(config, store)
|
||||||
|
repo.run_hook(repo.hooks[0][1], [])
|
||||||
|
with ruby.in_env(repo.cmd_runner, 'default') as env:
|
||||||
|
output = env.run('gem list --local')[1]
|
||||||
|
assert 'mime-types' in output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
def test_additional_node_dependencies_installed(tempdir_factory, store):
|
||||||
|
path = make_repo(tempdir_factory, 'node_hooks_repo')
|
||||||
|
config = make_config_from_repo(path)
|
||||||
|
# Careful to choose a small package that's not depped by npm
|
||||||
|
config['hooks'][0]['additional_dependencies'] = ['lodash']
|
||||||
|
repo = Repository.create(config, store)
|
||||||
|
repo.run_hook(repo.hooks[0][1], [])
|
||||||
|
with node.in_env(repo.cmd_runner, 'default') as env:
|
||||||
|
env.run('npm config set global true')
|
||||||
|
output = env.run(('npm ls'))[1]
|
||||||
|
assert 'lodash' in output
|
||||||
|
|
||||||
|
|
||||||
def test_reinstall(tempdir_factory, store, log_info_mock):
|
def test_reinstall(tempdir_factory, store, log_info_mock):
|
||||||
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
||||||
config = make_config_from_repo(path)
|
config = make_config_from_repo(path)
|
||||||
|
|
@ -350,7 +399,7 @@ def test_control_c_control_c_on_install(tempdir_factory, store):
|
||||||
# raise as well.
|
# raise as well.
|
||||||
with pytest.raises(MyKeyboardInterrupt):
|
with pytest.raises(MyKeyboardInterrupt):
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
PythonEnv, 'run', side_effect=MyKeyboardInterrupt,
|
python.PythonEnv, 'run', side_effect=MyKeyboardInterrupt,
|
||||||
):
|
):
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
shutil, 'rmtree', side_effect=MyKeyboardInterrupt,
|
shutil, 'rmtree', side_effect=MyKeyboardInterrupt,
|
||||||
|
|
@ -441,5 +490,5 @@ def test_norm_version_expanduser(): # pragma: no cover
|
||||||
else:
|
else:
|
||||||
path = '~/.pyenv/versions/3.4.3/bin/python'
|
path = '~/.pyenv/versions/3.4.3/bin/python'
|
||||||
expected_path = home + '/.pyenv/versions/3.4.3/bin/python'
|
expected_path = home + '/.pyenv/versions/3.4.3/bin/python'
|
||||||
result = norm_version(path)
|
result = python.norm_version(path)
|
||||||
assert result == expected_path
|
assert result == expected_path
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue