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