mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-02-17 08:14:42 +04:00
Make additional_dependencies rollforward safe
This commit is contained in:
parent
c1c3f3b571
commit
b85a674026
3 changed files with 67 additions and 14 deletions
|
|
@ -1,12 +1,16 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import io
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
from cached_property import cached_property
|
from cached_property import cached_property
|
||||||
|
|
||||||
|
from pre_commit import five
|
||||||
from pre_commit import git
|
from pre_commit import git
|
||||||
from pre_commit.clientlib.validate_config import is_local_hooks
|
from pre_commit.clientlib.validate_config import is_local_hooks
|
||||||
from pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA
|
from pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA
|
||||||
|
|
@ -23,6 +27,9 @@ _pre_commit_version = pkg_resources.parse_version(
|
||||||
pkg_resources.get_distribution('pre-commit').version
|
pkg_resources.get_distribution('pre-commit').version
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Bump when installation changes in a backwards / forwards incompatible way
|
||||||
|
INSTALLED_STATE_VERSION = '1'
|
||||||
|
|
||||||
|
|
||||||
class Repository(object):
|
class Repository(object):
|
||||||
def __init__(self, repo_config, repo_path_getter):
|
def __init__(self, repo_config, repo_path_getter):
|
||||||
|
|
@ -110,14 +117,45 @@ class Repository(object):
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
"""Install the hook repository."""
|
"""Install the hook repository."""
|
||||||
|
def state(language_name, language_version):
|
||||||
|
return {
|
||||||
|
'additional_dependencies': sorted(
|
||||||
|
self.additional_dependencies[
|
||||||
|
language_name
|
||||||
|
][language_version],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def state_filename(venv, suffix=''):
|
||||||
|
return self.cmd_runner.path(
|
||||||
|
venv, '.install_state_v' + INSTALLED_STATE_VERSION + suffix,
|
||||||
|
)
|
||||||
|
|
||||||
|
def read_state(venv):
|
||||||
|
if not os.path.exists(state_filename(venv)):
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return json.loads(io.open(state_filename(venv)).read())
|
||||||
|
|
||||||
|
def write_state(venv, language_name, language_version):
|
||||||
|
with io.open(
|
||||||
|
state_filename(venv, suffix='staging'), 'w',
|
||||||
|
) as state_file:
|
||||||
|
state_file.write(five.to_text(json.dumps(
|
||||||
|
state(language_name, language_version),
|
||||||
|
)))
|
||||||
|
# Move the file into place atomically to indicate we've installed
|
||||||
|
os.rename(
|
||||||
|
state_filename(venv, suffix='staging'),
|
||||||
|
state_filename(venv),
|
||||||
|
)
|
||||||
|
|
||||||
def language_is_installed(language_name, language_version):
|
def language_is_installed(language_name, language_version):
|
||||||
language = languages[language_name]
|
language = languages[language_name]
|
||||||
directory = environment_dir(
|
venv = environment_dir(language.ENVIRONMENT_DIR, language_version)
|
||||||
language.ENVIRONMENT_DIR, language_version,
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
directory is None or
|
venv is None or
|
||||||
self.cmd_runner.exists(directory, '.installed')
|
read_state(venv) == state(language_name, language_version)
|
||||||
)
|
)
|
||||||
|
|
||||||
if not all(
|
if not all(
|
||||||
|
|
@ -131,24 +169,23 @@ class Repository(object):
|
||||||
logger.info('This may take a few minutes...')
|
logger.info('This may take a few minutes...')
|
||||||
|
|
||||||
for language_name, language_version in self.languages:
|
for language_name, language_version in self.languages:
|
||||||
language = languages[language_name]
|
|
||||||
if language_is_installed(language_name, language_version):
|
if language_is_installed(language_name, language_version):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
directory = environment_dir(
|
language = languages[language_name]
|
||||||
language.ENVIRONMENT_DIR, language_version,
|
venv = environment_dir(language.ENVIRONMENT_DIR, language_version)
|
||||||
)
|
|
||||||
# There's potentially incomplete cleanup from previous runs
|
# There's potentially incomplete cleanup from previous runs
|
||||||
# Clean it up!
|
# Clean it up!
|
||||||
if self.cmd_runner.exists(directory):
|
if self.cmd_runner.exists(venv):
|
||||||
shutil.rmtree(self.cmd_runner.path(directory))
|
shutil.rmtree(self.cmd_runner.path(venv))
|
||||||
|
|
||||||
language.install_environment(
|
language.install_environment(
|
||||||
self.cmd_runner, language_version,
|
self.cmd_runner, language_version,
|
||||||
self.additional_dependencies[language_name][language_version],
|
self.additional_dependencies[language_name][language_version],
|
||||||
)
|
)
|
||||||
# Touch the .installed file (atomic) to indicate we've installed
|
# Write our state to indicate we're installed
|
||||||
open(self.cmd_runner.path(directory, '.installed'), 'w').close()
|
write_state(venv, language_name, language_version)
|
||||||
|
|
||||||
def run_hook(self, hook, file_args):
|
def run_hook(self, hook, file_args):
|
||||||
"""Run a hook.
|
"""Run a hook.
|
||||||
|
|
|
||||||
1
setup.py
1
setup.py
|
|
@ -46,7 +46,6 @@ setup(
|
||||||
'nodeenv>=0.11.1',
|
'nodeenv>=0.11.1',
|
||||||
'ordereddict',
|
'ordereddict',
|
||||||
'pyyaml',
|
'pyyaml',
|
||||||
'simplejson',
|
|
||||||
'virtualenv',
|
'virtualenv',
|
||||||
],
|
],
|
||||||
entry_points={
|
entry_points={
|
||||||
|
|
|
||||||
|
|
@ -360,6 +360,23 @@ def test_additional_python_dependencies_installed(tempdir_factory, store):
|
||||||
assert 'mccabe' in output
|
assert 'mccabe' in output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
def test_additional_dependencies_roll_forward(tempdir_factory, store):
|
||||||
|
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
||||||
|
config = make_config_from_repo(path)
|
||||||
|
# Run the repo once without additional_dependencies
|
||||||
|
repo = Repository.create(config, store)
|
||||||
|
repo.run_hook(repo.hooks[0][1], [])
|
||||||
|
# Now run it with additional_dependencies
|
||||||
|
config['hooks'][0]['additional_dependencies'] = ['mccabe']
|
||||||
|
repo = Repository.create(config, store)
|
||||||
|
repo.run_hook(repo.hooks[0][1], [])
|
||||||
|
# We should see our additional dependency installed
|
||||||
|
with python.in_env(repo.cmd_runner, 'default') as env:
|
||||||
|
output = env.run('pip freeze -l')[1]
|
||||||
|
assert 'mccabe' in output
|
||||||
|
|
||||||
|
|
||||||
@xfailif_windows_no_ruby
|
@xfailif_windows_no_ruby
|
||||||
@pytest.mark.integration
|
@pytest.mark.integration
|
||||||
def test_additional_ruby_dependencies_installed(
|
def test_additional_ruby_dependencies_installed(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue