1from __future__ import unicode_literals 2 3import contextlib 4import io 5import os.path 6import shutil 7import tarfile 8 9import pre_commit.constants as C 10from pre_commit.envcontext import envcontext 11from pre_commit.envcontext import Var 12from pre_commit.languages import helpers 13from pre_commit.util import CalledProcessError 14from pre_commit.util import clean_path_on_failure 15from pre_commit.util import resource_bytesio 16 17 18ENVIRONMENT_DIR = 'rbenv' 19get_default_version = helpers.basic_get_default_version 20healthy = helpers.basic_healthy 21 22 23def get_env_patch(venv, language_version): # pragma: windows no cover 24 patches = ( 25 ('GEM_HOME', os.path.join(venv, 'gems')), 26 ('RBENV_ROOT', venv), 27 ('BUNDLE_IGNORE_CONFIG', '1'), 28 ( 29 'PATH', ( 30 os.path.join(venv, 'gems', 'bin'), os.pathsep, 31 os.path.join(venv, 'shims'), os.pathsep, 32 os.path.join(venv, 'bin'), os.pathsep, Var('PATH'), 33 ), 34 ), 35 ) 36 if language_version != C.DEFAULT: 37 patches += (('RBENV_VERSION', language_version),) 38 return patches 39 40 41@contextlib.contextmanager 42def in_env(prefix, language_version): # pragma: windows no cover 43 envdir = prefix.path( 44 helpers.environment_dir(ENVIRONMENT_DIR, language_version), 45 ) 46 with envcontext(get_env_patch(envdir, language_version)): 47 yield 48 49 50def _extract_resource(filename, dest): 51 with resource_bytesio(filename) as bio: 52 with tarfile.open(fileobj=bio) as tf: 53 tf.extractall(dest) 54 55 56def _install_rbenv(prefix, version=C.DEFAULT): # pragma: windows no cover 57 directory = helpers.environment_dir(ENVIRONMENT_DIR, version) 58 59 _extract_resource('rbenv.tar.gz', prefix.path('.')) 60 shutil.move(prefix.path('rbenv'), prefix.path(directory)) 61 62 # Only install ruby-build if the version is specified 63 if version != C.DEFAULT: 64 plugins_dir = prefix.path(directory, 'plugins') 65 _extract_resource('ruby-download.tar.gz', plugins_dir) 66 _extract_resource('ruby-build.tar.gz', plugins_dir) 67 68 activate_path = prefix.path(directory, 'bin', 'activate') 69 with io.open(activate_path, 'w') as activate_file: 70 # This is similar to how you would install rbenv to your home directory 71 # However we do a couple things to make the executables exposed and 72 # configure it to work in our directory. 73 # We also modify the PS1 variable for manual debugging sake. 74 activate_file.write( 75 '#!/usr/bin/env bash\n' 76 "export RBENV_ROOT='{directory}'\n" 77 'export PATH="$RBENV_ROOT/bin:$PATH"\n' 78 'eval "$(rbenv init -)"\n' 79 'export PS1="(rbenv)$PS1"\n' 80 # This lets us install gems in an isolated and repeatable 81 # directory 82 "export GEM_HOME='{directory}/gems'\n" 83 'export PATH="$GEM_HOME/bin:$PATH"\n' 84 '\n'.format(directory=prefix.path(directory)), 85 ) 86 87 # If we aren't using the system ruby, add a version here 88 if version != C.DEFAULT: 89 activate_file.write('export RBENV_VERSION="{}"\n'.format(version)) 90 91 92def _install_ruby(prefix, version): # pragma: windows no cover 93 try: 94 helpers.run_setup_cmd(prefix, ('rbenv', 'download', version)) 95 except CalledProcessError: # pragma: no cover (usually find with download) 96 # Failed to download from mirror for some reason, build it instead 97 helpers.run_setup_cmd(prefix, ('rbenv', 'install', version)) 98 99 100def install_environment( 101 prefix, version, additional_dependencies, 102): # pragma: windows no cover 103 additional_dependencies = tuple(additional_dependencies) 104 directory = helpers.environment_dir(ENVIRONMENT_DIR, version) 105 with clean_path_on_failure(prefix.path(directory)): 106 # TODO: this currently will fail if there's no version specified and 107 # there's no system ruby installed. Is this ok? 108 _install_rbenv(prefix, version=version) 109 with in_env(prefix, version): 110 # Need to call this before installing so rbenv's directories are 111 # set up 112 helpers.run_setup_cmd(prefix, ('rbenv', 'init', '-')) 113 if version != C.DEFAULT: 114 _install_ruby(prefix, version) 115 # Need to call this after installing to set up the shims 116 helpers.run_setup_cmd(prefix, ('rbenv', 'rehash')) 117 helpers.run_setup_cmd( 118 prefix, ('gem', 'build') + prefix.star('.gemspec'), 119 ) 120 helpers.run_setup_cmd( 121 prefix, 122 ('gem', 'install', '--no-document') + 123 prefix.star('.gem') + additional_dependencies, 124 ) 125 126 127def run_hook(hook, file_args, color): # pragma: windows no cover 128 with in_env(hook.prefix, hook.language_version): 129 return helpers.run_xargs(hook, hook.cmd, file_args, color=color) 130