From 45d4a195efd414cc04ea718989e0a79e2da1df0b Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 16 May 2015 19:16:23 -0400 Subject: [PATCH] Environments are now installed to version-specific locations. Resolves #229 --- pre_commit/git.py | 5 +++-- pre_commit/languages/helpers.py | 10 +++++++++- pre_commit/languages/node.py | 17 ++++++++++------- pre_commit/languages/python.py | 15 ++++++++------- pre_commit/languages/ruby.py | 29 +++++++++++++++++++---------- pre_commit/repository.py | 28 ++++++++++++++++------------ tests/languages/ruby_test.py | 8 ++++---- tests/repository_test.py | 2 +- tox.ini | 1 + 9 files changed, 71 insertions(+), 44 deletions(-) diff --git a/pre_commit/git.py b/pre_commit/git.py index f16875c0..83714bde 100644 --- a/pre_commit/git.py +++ b/pre_commit/git.py @@ -37,9 +37,10 @@ def is_in_merge_conflict(): def parse_merge_msg_for_conflicts(merge_msg): # Conflicted files start with tabs return [ - line.strip() + line.lstrip('#').strip() for line in merge_msg.splitlines() - if line.startswith('\t') + # '#\t' for git 2.4.1 + if line.startswith(('\t', '#\t')) ] diff --git a/pre_commit/languages/helpers.py b/pre_commit/languages/helpers.py index ff46acc4..ffbe66ac 100644 --- a/pre_commit/languages/helpers.py +++ b/pre_commit/languages/helpers.py @@ -3,6 +3,13 @@ from __future__ import unicode_literals import pipes +def environment_dir(ENVIRONMENT_DIR, language_version): + if ENVIRONMENT_DIR is None: + return None + else: + return '{0}-{1}'.format(ENVIRONMENT_DIR, language_version) + + def file_args_to_stdin(file_args): return '\0'.join(list(file_args) + ['']) @@ -19,8 +26,9 @@ def run_hook(env, hook, file_args): class Environment(object): - def __init__(self, repo_cmd_runner): + def __init__(self, repo_cmd_runner, language_version): self.repo_cmd_runner = repo_cmd_runner + self.language_version = language_version @property def env_prefix(self): diff --git a/pre_commit/languages/node.py b/pre_commit/languages/node.py index 646a8920..20fa8572 100644 --- a/pre_commit/languages/node.py +++ b/pre_commit/languages/node.py @@ -13,22 +13,25 @@ ENVIRONMENT_DIR = 'node_env' class NodeEnv(helpers.Environment): @property def env_prefix(self): - return ". '{{prefix}}{0}/bin/activate' &&".format(ENVIRONMENT_DIR) + return ". '{{prefix}}{0}/bin/activate' &&".format( + helpers.environment_dir(ENVIRONMENT_DIR, self.language_version), + ) @contextlib.contextmanager -def in_env(repo_cmd_runner): - yield NodeEnv(repo_cmd_runner) +def in_env(repo_cmd_runner, language_version): + yield NodeEnv(repo_cmd_runner, language_version) def install_environment(repo_cmd_runner, version='default'): assert repo_cmd_runner.exists('package.json') + directory = helpers.environment_dir(ENVIRONMENT_DIR, version) - env_dir = repo_cmd_runner.path(ENVIRONMENT_DIR) + env_dir = repo_cmd_runner.path(directory) with clean_path_on_failure(env_dir): cmd = [ sys.executable, '-m', 'nodeenv', '--prebuilt', - '{{prefix}}{0}'.format(ENVIRONMENT_DIR), + '{{prefix}}{0}'.format(directory), ] if version != 'default': @@ -36,10 +39,10 @@ def install_environment(repo_cmd_runner, version='default'): repo_cmd_runner.run(cmd) - with in_env(repo_cmd_runner) as node_env: + with in_env(repo_cmd_runner, version) as node_env: node_env.run("cd '{prefix}' && npm install -g") def run_hook(repo_cmd_runner, hook, file_args): - with in_env(repo_cmd_runner) as env: + with in_env(repo_cmd_runner, hook['language_version']) as env: return helpers.run_hook(env, hook, file_args) diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py index 7c7d9a47..94630f5a 100644 --- a/pre_commit/languages/python.py +++ b/pre_commit/languages/python.py @@ -19,15 +19,15 @@ class PythonEnv(helpers.Environment): def env_prefix(self): return ". '{{prefix}}{0}activate' &&".format( virtualenv.path_locations( - ENVIRONMENT_DIR, + helpers.environment_dir(ENVIRONMENT_DIR, self.language_version) )[-1].rstrip(os.sep) + os.sep, 'activate', ) @contextlib.contextmanager -def in_env(repo_cmd_runner): - yield PythonEnv(repo_cmd_runner) +def in_env(repo_cmd_runner, language_version): + yield PythonEnv(repo_cmd_runner, language_version) def norm_version(version): @@ -41,20 +41,21 @@ def norm_version(version): def install_environment(repo_cmd_runner, version='default'): assert repo_cmd_runner.exists('setup.py') + directory = helpers.environment_dir(ENVIRONMENT_DIR, version) # Install a virtualenv - with clean_path_on_failure(repo_cmd_runner.path(ENVIRONMENT_DIR)): + with clean_path_on_failure(repo_cmd_runner.path(directory)): venv_cmd = [ sys.executable, '-m', 'virtualenv', - '{{prefix}}{0}'.format(ENVIRONMENT_DIR) + '{{prefix}}{0}'.format(directory) ] if version != 'default': venv_cmd.extend(['-p', norm_version(version)]) repo_cmd_runner.run(venv_cmd) - with in_env(repo_cmd_runner) as env: + with in_env(repo_cmd_runner, version) as env: env.run("cd '{prefix}' && pip install .") def run_hook(repo_cmd_runner, hook, file_args): - with in_env(repo_cmd_runner) as env: + with in_env(repo_cmd_runner, hook['language_version']) as env: return helpers.run_hook(env, hook, file_args) diff --git a/pre_commit/languages/ruby.py b/pre_commit/languages/ruby.py index 94e0f574..18f304e5 100644 --- a/pre_commit/languages/ruby.py +++ b/pre_commit/languages/ruby.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import contextlib import io +import shutil from pre_commit.languages import helpers from pre_commit.util import CalledProcessError @@ -16,29 +17,36 @@ ENVIRONMENT_DIR = 'rbenv' class RubyEnv(helpers.Environment): @property def env_prefix(self): - return '. {{prefix}}{0}/bin/activate &&'.format(ENVIRONMENT_DIR) + return '. {{prefix}}{0}/bin/activate &&'.format( + helpers.environment_dir(ENVIRONMENT_DIR, self.language_version) + ) @contextlib.contextmanager -def in_env(repo_cmd_runner): - yield RubyEnv(repo_cmd_runner) +def in_env(repo_cmd_runner, language_version): + yield RubyEnv(repo_cmd_runner, language_version) def _install_rbenv(repo_cmd_runner, version='default'): + directory = helpers.environment_dir(ENVIRONMENT_DIR, version) + with tarfile_open(resource_filename('rbenv.tar.gz')) as tf: tf.extractall(repo_cmd_runner.path('.')) + shutil.move( + repo_cmd_runner.path('rbenv'), repo_cmd_runner.path(directory), + ) # Only install ruby-build if the version is specified if version != 'default': # ruby-download with tarfile_open(resource_filename('ruby-download.tar.gz')) as tf: - tf.extractall(repo_cmd_runner.path('rbenv', 'plugins')) + tf.extractall(repo_cmd_runner.path(directory, 'plugins')) # ruby-build with tarfile_open(resource_filename('ruby-build.tar.gz')) as tf: - tf.extractall(repo_cmd_runner.path('rbenv', 'plugins')) + tf.extractall(repo_cmd_runner.path(directory, 'plugins')) - activate_path = repo_cmd_runner.path('rbenv', 'bin', 'activate') + activate_path = repo_cmd_runner.path(directory, 'bin', 'activate') with io.open(activate_path, 'w') as activate_file: # This is similar to how you would install rbenv to your home directory # However we do a couple things to make the executables exposed and @@ -54,7 +62,7 @@ def _install_rbenv(repo_cmd_runner, version='default'): # directory "export GEM_HOME='{0}/gems'\n" 'export PATH="$GEM_HOME/bin:$PATH"\n' - '\n'.format(repo_cmd_runner.path('rbenv')) + '\n'.format(repo_cmd_runner.path(directory)) ) # If we aren't using the system ruby, add a version here @@ -71,11 +79,12 @@ def _install_ruby(environment, version): def install_environment(repo_cmd_runner, version='default'): - with clean_path_on_failure(repo_cmd_runner.path('rbenv')): + directory = helpers.environment_dir(ENVIRONMENT_DIR, version) + with clean_path_on_failure(repo_cmd_runner.path(directory)): # TODO: this currently will fail if there's no version specified and # there's no system ruby installed. Is this ok? _install_rbenv(repo_cmd_runner, version=version) - with in_env(repo_cmd_runner) as ruby_env: + with in_env(repo_cmd_runner, version) as ruby_env: if version != 'default': _install_ruby(ruby_env, version) ruby_env.run( @@ -84,5 +93,5 @@ def install_environment(repo_cmd_runner, version='default'): def run_hook(repo_cmd_runner, hook, file_args): - with in_env(repo_cmd_runner) as env: + with in_env(repo_cmd_runner, hook['language_version']) as env: return helpers.run_hook(env, hook, file_args) diff --git a/pre_commit/repository.py b/pre_commit/repository.py index 7ca6a442..71cc3569 100644 --- a/pre_commit/repository.py +++ b/pre_commit/repository.py @@ -10,6 +10,7 @@ from pre_commit.clientlib.validate_config import is_local_hooks from pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA from pre_commit.jsonschema_extensions import apply_defaults from pre_commit.languages.all import languages +from pre_commit.languages.helpers import environment_dir from pre_commit.manifest import Manifest from pre_commit.prefixed_command_runner import PrefixedCommandRunner @@ -73,16 +74,19 @@ class Repository(object): def install(self): """Install the hook repository.""" - def language_is_installed(language_name): + def language_is_installed(language_name, language_version): language = languages[language_name] + directory = environment_dir( + language.ENVIRONMENT_DIR, language_version, + ) return ( - language.ENVIRONMENT_DIR is None or - self.cmd_runner.exists(language.ENVIRONMENT_DIR, '.installed') + directory is None or + self.cmd_runner.exists(directory, '.installed') ) if not all( - language_is_installed(language_name) - for language_name, _ in self.languages + language_is_installed(language_name, language_version) + for language_name, language_version in self.languages ): logger.info( 'Installing environment for {0}.'.format(self.repo_url) @@ -92,20 +96,20 @@ class Repository(object): for language_name, language_version in self.languages: language = languages[language_name] - if language_is_installed(language_name): + if language_is_installed(language_name, language_version): continue + directory = environment_dir( + language.ENVIRONMENT_DIR, language_version, + ) # There's potentially incomplete cleanup from previous runs # Clean it up! - if self.cmd_runner.exists(language.ENVIRONMENT_DIR): - shutil.rmtree(self.cmd_runner.path(language.ENVIRONMENT_DIR)) + if self.cmd_runner.exists(directory): + shutil.rmtree(self.cmd_runner.path(directory)) language.install_environment(self.cmd_runner, language_version) # Touch the .installed file (atomic) to indicate we've installed - open( - self.cmd_runner.path(language.ENVIRONMENT_DIR, '.installed'), - 'w', - ).close() + open(self.cmd_runner.path(directory, '.installed'), 'w').close() def run_hook(self, hook, file_args): """Run a hook. diff --git a/tests/languages/ruby_test.py b/tests/languages/ruby_test.py index 9499fcee..1eddea1d 100644 --- a/tests/languages/ruby_test.py +++ b/tests/languages/ruby_test.py @@ -10,9 +10,9 @@ from testing.util import xfailif_windows_no_ruby def test_install_rbenv(cmd_runner): _install_rbenv(cmd_runner) # Should have created rbenv directory - assert os.path.exists(cmd_runner.path('rbenv')) + assert os.path.exists(cmd_runner.path('rbenv-default')) # We should have created our `activate` script - activate_path = cmd_runner.path('rbenv', 'bin', 'activate') + activate_path = cmd_runner.path('rbenv-default', 'bin', 'activate') assert os.path.exists(activate_path) # Should be able to activate using our script and access rbenv @@ -20,7 +20,7 @@ def test_install_rbenv(cmd_runner): [ 'bash', '-c', - ". '{prefix}rbenv/bin/activate' && rbenv --help", + ". '{prefix}rbenv-default/bin/activate' && rbenv --help", ], ) @@ -34,6 +34,6 @@ def test_install_rbenv_with_version(cmd_runner): [ 'bash', '-c', - ". '{prefix}rbenv/bin/activate' && rbenv install --help", + ". '{prefix}rbenv-1.9.3p547/bin/activate' && rbenv install --help", ], ) diff --git a/tests/repository_test.py b/tests/repository_test.py index e8cd0cee..f2e88507 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -338,7 +338,7 @@ def test_control_c_control_c_on_install(tmpdir_factory, store): repo.run_hook(hook, []) # Should have made an environment, however this environment is broken! - assert os.path.exists(repo.cmd_runner.path('py_env')) + assert os.path.exists(repo.cmd_runner.path('py_env-default')) # However, it should be perfectly runnable (reinstall after botched # install) diff --git a/tox.ini b/tox.ini index 919cdf02..07c56589 100644 --- a/tox.ini +++ b/tox.ini @@ -6,6 +6,7 @@ envlist = py26,py27,py33,py34,pypy [testenv] install_command = pip install --use-wheel {opts} {packages} deps = -rrequirements-dev.txt +passenv = HOME HOMEPATH LANG TERM commands = coverage erase coverage run -m pytest {posargs:tests}