From e68ffbfacafb6f13533a01c94183df84e4a2a995 Mon Sep 17 00:00:00 2001 From: Ben Picolo Date: Tue, 3 Jan 2017 14:00:16 -0500 Subject: [PATCH] Add PHP language support --- pre_commit/languages/all.py | 2 + pre_commit/languages/php.py | 131 ++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 pre_commit/languages/php.py diff --git a/pre_commit/languages/all.py b/pre_commit/languages/all.py index 40c23131..3e8cbaaa 100644 --- a/pre_commit/languages/all.py +++ b/pre_commit/languages/all.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from pre_commit.languages import node from pre_commit.languages import pcre +from pre_commit.languages import php from pre_commit.languages import python from pre_commit.languages import ruby from pre_commit.languages import script @@ -42,6 +43,7 @@ from pre_commit.languages import system languages = { 'node': node, 'pcre': pcre, + 'php': php, 'python': python, 'ruby': ruby, 'script': script, diff --git a/pre_commit/languages/php.py b/pre_commit/languages/php.py new file mode 100644 index 00000000..290403cf --- /dev/null +++ b/pre_commit/languages/php.py @@ -0,0 +1,131 @@ +from __future__ import unicode_literals + +import contextlib +import json +import os + +from pre_commit.envcontext import envcontext +from pre_commit.envcontext import Var +from pre_commit.languages import helpers +from pre_commit.util import clean_path_on_failure +from pre_commit.util import resource_filename +from pre_commit.xargs import xargs + + +ENVIRONMENT_DIR = 'virtphpenv' +VIRT_PHP_PATH = resource_filename('virtphp.phar') +# It's currently impossible to move this file out of homedir +VIRT_PHP_ENVIRONMENTS = os.path.join( + os.path.expanduser('~'), + '.virtphp', + 'environments.json' +) + + +def get_env_patch(venv, language_version): + patches = ( + ('VIRTPHP_COMPOSER_GLOBAL', '1'), + ('COMPOSER_HOME', os.path.join(venv, 'composer')), + ('PHP_INI_SCAN_DIR', os.path.join(venv, 'etc', 'php')), + ('PATH', ( + os.path.join(venv, 'bin'), os.pathsep, + os.path.join(venv, 'vendor/bin'), os.pathsep, Var('PATH') + )) + ) + + return patches + + +@contextlib.contextmanager +def in_env(repo_cmd_runner, language_version): + envdir = os.path.join( + repo_cmd_runner.prefix_dir, + helpers.environment_dir(ENVIRONMENT_DIR, language_version), + ) + + with envcontext(get_env_patch(envdir, language_version)): + yield + + +@contextlib.contextmanager +def cleanup_environment_on_failure(repo_cmd_runner, directory): + with clean_path_on_failure(repo_cmd_runner.path(directory)): + with clean_virtphp_environment_on_failure(directory): + yield + + +@contextlib.contextmanager +def clean_virtphp_environment_on_failure(directory): + """VirtPHP keeps an environments.json file that lists the envs that it + thinks exists, and it will fail to install them if it thinks that one + exists, even if it does not. + This file looks like: + { + "virtenv-default": {} + } + """ + try: + yield + except BaseException: + if os.path.exists(VIRT_PHP_ENVIRONMENTS): + with open(VIRT_PHP_ENVIRONMENTS, 'r+') as f: + environments = json.loads(f.read()) + environments = { + environment_key: environment_config + for environment_key, environment_config + in environments.items() if environment_key != directory + } + f.seek(0) + f.write(json.dumps(environments)) + f.truncate() + + raise + + +def install_environment( + repo_cmd_runner, + version='default', + additional_dependencies=(), +): + additional_dependencies = tuple(additional_dependencies) + directory = helpers.environment_dir(ENVIRONMENT_DIR, version) + + with cleanup_environment_on_failure(repo_cmd_runner, directory): + virtphp_env_command = [ + 'php', VIRT_PHP_PATH, 'create', + '--install-path', repo_cmd_runner.prefix_dir[:-1], + directory + ] + + if version != 'default': + # TODO: Not sure what the best choice is here. There's doesn't seem + # to be a canonical way to keep multiple php versions around. + # It's Different per OS, package manager, etc. language_version + # as a php bin directory (as shown here) might be sanest? + virtphp_env_command.extend( + ['--php-bin-dir', os.path.realpath(version)] + ) + + repo_cmd_runner.run(virtphp_env_command, cwd='/') + with in_env(repo_cmd_runner, version): + helpers.run_setup_cmd( + repo_cmd_runner, + # There's no way to install platform requirements out of band + # as we're creating the php environment via pre-commit, nor is + # there a generalized way to install them. If hooks + # need platform requirements, they should install it themselves + # in a pre-install composer hook. Therefore, we ignore + # platform requirements for composer installation. + ( + 'composer', + 'install', + '--ignore-platform-reqs' + ) + additional_dependencies + ) + + +def run_hook(repo_cmd_runner, hook, file_args): + return xargs( + (repo_cmd_runner.prefix_dir + hook['entry'],) + tuple(hook['args']), + file_args, + )