mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-04-16 02:21:46 +04:00
Activate conda environment in pre-commit hook.
Save conda environment that was active during conda install when using option --hooks-activate-conda. The saved environment will be activated before calling pre-commit hooks. Especially on Windows, more and more actions within a conda environment require the conda environment to be activated. Thus saving just the python executable is not enough any more. There is currently one downside of using the option --hooks-activate-conda. It uses "conda run" which will only show console output after the run is completed. We have a pull request to conda open which introduces an option to show interactive console output in conda run. Once this is approved, it might be ok to make this option the default behaviour.
This commit is contained in:
parent
f0ee93c5a7
commit
dbdba3c67f
4 changed files with 92 additions and 16 deletions
|
|
@ -73,6 +73,7 @@ def _install_hook_script(
|
||||||
hook_type: str,
|
hook_type: str,
|
||||||
overwrite: bool = False,
|
overwrite: bool = False,
|
||||||
skip_on_missing_config: bool = False,
|
skip_on_missing_config: bool = False,
|
||||||
|
hooks_activate_conda: bool = False,
|
||||||
git_dir: Optional[str] = None,
|
git_dir: Optional[str] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
hook_path, legacy_path = _hook_paths(hook_type, git_dir=git_dir)
|
hook_path, legacy_path = _hook_paths(hook_type, git_dir=git_dir)
|
||||||
|
|
@ -95,7 +96,20 @@ def _install_hook_script(
|
||||||
args = ['hook-impl', f'--config={config_file}', f'--hook-type={hook_type}']
|
args = ['hook-impl', f'--config={config_file}', f'--hook-type={hook_type}']
|
||||||
if skip_on_missing_config:
|
if skip_on_missing_config:
|
||||||
args.append('--skip-on-missing-config')
|
args.append('--skip-on-missing-config')
|
||||||
params = {'INSTALL_PYTHON': sys.executable, 'ARGS': args}
|
params = {
|
||||||
|
'INSTALL_PYTHON': sys.executable, 'INSTALL_CONDA': '',
|
||||||
|
'INSTALL_CONDA_PREFIX': '', 'ARGS': args,
|
||||||
|
}
|
||||||
|
if hooks_activate_conda:
|
||||||
|
if 'CONDA_EXE' in os.environ and 'CONDA_PREFIX' in os.environ:
|
||||||
|
params['INSTALL_PYTHON'] = '' # conda will find correct python
|
||||||
|
params['INSTALL_CONDA'] = os.getenv('CONDA_EXE', '')
|
||||||
|
params['INSTALL_CONDA_PREFIX'] = os.getenv('CONDA_PREFIX', '')
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
'Failed to detect activated conda, '
|
||||||
|
'ignoring option --hooks_activate_conda.',
|
||||||
|
)
|
||||||
|
|
||||||
with open(hook_path, 'w') as hook_file:
|
with open(hook_path, 'w') as hook_file:
|
||||||
contents = resource_text('hook-tmpl')
|
contents = resource_text('hook-tmpl')
|
||||||
|
|
@ -121,6 +135,7 @@ def install(
|
||||||
overwrite: bool = False,
|
overwrite: bool = False,
|
||||||
hooks: bool = False,
|
hooks: bool = False,
|
||||||
skip_on_missing_config: bool = False,
|
skip_on_missing_config: bool = False,
|
||||||
|
hooks_activate_conda: bool = False,
|
||||||
git_dir: Optional[str] = None,
|
git_dir: Optional[str] = None,
|
||||||
) -> int:
|
) -> int:
|
||||||
if git_dir is None and git.has_core_hookpaths_set():
|
if git_dir is None and git.has_core_hookpaths_set():
|
||||||
|
|
@ -135,6 +150,7 @@ def install(
|
||||||
config_file, hook_type,
|
config_file, hook_type,
|
||||||
overwrite=overwrite,
|
overwrite=overwrite,
|
||||||
skip_on_missing_config=skip_on_missing_config,
|
skip_on_missing_config=skip_on_missing_config,
|
||||||
|
hooks_activate_conda=hooks_activate_conda,
|
||||||
git_dir=git_dir,
|
git_dir=git_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,13 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||||
'or exit with a failure code.'
|
'or exit with a failure code.'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
install_parser.add_argument(
|
||||||
|
'--hooks-activate-conda', action='store_true', default=False,
|
||||||
|
help=(
|
||||||
|
'Whether to activate conda environment before calling pre-commit'
|
||||||
|
'within hooks.'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
install_hooks_parser = subparsers.add_parser(
|
install_hooks_parser = subparsers.add_parser(
|
||||||
'install-hooks',
|
'install-hooks',
|
||||||
|
|
@ -357,6 +364,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||||
overwrite=args.overwrite,
|
overwrite=args.overwrite,
|
||||||
hooks=args.install_hooks,
|
hooks=args.install_hooks,
|
||||||
skip_on_missing_config=args.allow_missing_config,
|
skip_on_missing_config=args.allow_missing_config,
|
||||||
|
hooks_activate_conda=args.hooks_activate_conda,
|
||||||
)
|
)
|
||||||
elif args.command == 'init-templatedir':
|
elif args.command == 'init-templatedir':
|
||||||
return init_templatedir(
|
return init_templatedir(
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ os.environ.pop('__PYVENV_LAUNCHER__', None)
|
||||||
|
|
||||||
# start templated
|
# start templated
|
||||||
INSTALL_PYTHON = ''
|
INSTALL_PYTHON = ''
|
||||||
|
INSTALL_CONDA = ''
|
||||||
|
INSTALL_CONDA_PREFIX = ''
|
||||||
ARGS = ['hook-impl']
|
ARGS = ['hook-impl']
|
||||||
# end templated
|
# end templated
|
||||||
ARGS.extend(('--hook-dir', os.path.realpath(os.path.dirname(__file__))))
|
ARGS.extend(('--hook-dir', os.path.realpath(os.path.dirname(__file__))))
|
||||||
|
|
@ -25,7 +27,9 @@ ARGS.append('--')
|
||||||
ARGS.extend(sys.argv[1:])
|
ARGS.extend(sys.argv[1:])
|
||||||
|
|
||||||
DNE = '`pre-commit` not found. Did you forget to activate your virtualenv?'
|
DNE = '`pre-commit` not found. Did you forget to activate your virtualenv?'
|
||||||
if os.access(INSTALL_PYTHON, os.X_OK):
|
if os.access(INSTALL_CONDA, os.X_OK):
|
||||||
|
CMD = [INSTALL_CONDA, 'run', '-p', INSTALL_CONDA_PREFIX, 'pre-commit']
|
||||||
|
elif os.access(INSTALL_PYTHON, os.X_OK):
|
||||||
CMD = [INSTALL_PYTHON, '-mpre_commit']
|
CMD = [INSTALL_PYTHON, '-mpre_commit']
|
||||||
elif which('pre-commit'):
|
elif which('pre-commit'):
|
||||||
CMD = ['pre-commit']
|
CMD = ['pre-commit']
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,9 @@ FILES_CHANGED = (
|
||||||
NORMAL_PRE_COMMIT_RUN = re.compile(
|
NORMAL_PRE_COMMIT_RUN = re.compile(
|
||||||
fr'^\[INFO\] Initializing environment for .+\.\n'
|
fr'^\[INFO\] Initializing environment for .+\.\n'
|
||||||
fr'Bash hook\.+Passed\n'
|
fr'Bash hook\.+Passed\n'
|
||||||
|
fr'(\n)?'
|
||||||
fr'\[master [a-f0-9]{{7}}\] commit!\n'
|
fr'\[master [a-f0-9]{{7}}\] commit!\n'
|
||||||
|
fr'( Author: .*\n)?'
|
||||||
fr'{FILES_CHANGED}'
|
fr'{FILES_CHANGED}'
|
||||||
fr' create mode 100644 foo\n$',
|
fr' create mode 100644 foo\n$',
|
||||||
)
|
)
|
||||||
|
|
@ -487,6 +489,35 @@ def test_install_hooks_command(tempdir_factory, store):
|
||||||
assert PRE_INSTALLED.match(output)
|
assert PRE_INSTALLED.match(output)
|
||||||
|
|
||||||
|
|
||||||
|
def get_environment_without_pre_commit():
|
||||||
|
return {
|
||||||
|
'HOME': os.path.expanduser('~'),
|
||||||
|
'PATH': _path_without_us(),
|
||||||
|
'TERM': os.environ.get('TERM', ''),
|
||||||
|
# Windows needs this to import `random`
|
||||||
|
'SYSTEMROOT': os.environ.get('SYSTEMROOT', ''),
|
||||||
|
# Windows needs this to resolve executables
|
||||||
|
'PATHEXT': os.environ.get('PATHEXT', ''),
|
||||||
|
# Git needs this to make a commit
|
||||||
|
'GIT_AUTHOR_NAME': os.getenv(
|
||||||
|
'GIT_AUTHOR_NAME',
|
||||||
|
'author_name',
|
||||||
|
),
|
||||||
|
'GIT_COMMITTER_NAME': os.getenv(
|
||||||
|
'GIT_COMMITTER_NAME',
|
||||||
|
'committer_name',
|
||||||
|
),
|
||||||
|
'GIT_AUTHOR_EMAIL': os.getenv(
|
||||||
|
'GIT_AUTHOR_EMAIL',
|
||||||
|
'author@his.email',
|
||||||
|
),
|
||||||
|
'GIT_COMMITTER_EMAIL': os.getenv(
|
||||||
|
'GIT_COMMITTER_EMAIL',
|
||||||
|
'committer@his.email',
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_installed_from_venv(tempdir_factory, store):
|
def test_installed_from_venv(tempdir_factory, store):
|
||||||
path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
|
path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
|
||||||
with cwd(path):
|
with cwd(path):
|
||||||
|
|
@ -495,20 +526,37 @@ def test_installed_from_venv(tempdir_factory, store):
|
||||||
# Should still pick up the python from when we installed
|
# Should still pick up the python from when we installed
|
||||||
ret, output = _get_commit_output(
|
ret, output = _get_commit_output(
|
||||||
tempdir_factory,
|
tempdir_factory,
|
||||||
env={
|
env=get_environment_without_pre_commit(),
|
||||||
'HOME': os.path.expanduser('~'),
|
)
|
||||||
'PATH': _path_without_us(),
|
assert ret == 0
|
||||||
'TERM': os.environ.get('TERM', ''),
|
assert NORMAL_PRE_COMMIT_RUN.match(output)
|
||||||
# Windows needs this to import `random`
|
|
||||||
'SYSTEMROOT': os.environ.get('SYSTEMROOT', ''),
|
|
||||||
# Windows needs this to resolve executables
|
def test_installed_from_conda_env(tempdir_factory, store):
|
||||||
'PATHEXT': os.environ.get('PATHEXT', ''),
|
path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
|
||||||
# Git needs this to make a commit
|
with cwd(path):
|
||||||
'GIT_AUTHOR_NAME': os.environ['GIT_AUTHOR_NAME'],
|
conda_environment = tempdir_factory.get()
|
||||||
'GIT_COMMITTER_NAME': os.environ['GIT_COMMITTER_NAME'],
|
# Create conda environment in tempdir and simulate environment
|
||||||
'GIT_AUTHOR_EMAIL': os.environ['GIT_AUTHOR_EMAIL'],
|
# variables CONDA_EXE/PREFIX as if install() were called with
|
||||||
'GIT_COMMITTER_EMAIL': os.environ['GIT_COMMITTER_EMAIL'],
|
# activated conda from tmpdir
|
||||||
},
|
cmd_output(
|
||||||
|
'conda', 'run', 'conda', 'create', '-p', conda_environment, '-y',
|
||||||
|
'-c', 'conda-forge', 'pre-commit',
|
||||||
|
)
|
||||||
|
os.environ['CONDA_EXE'] = cmd_output(
|
||||||
|
'conda', 'run', '-p', conda_environment,
|
||||||
|
'echo', '$CONDA_EXE',
|
||||||
|
)[1].strip()
|
||||||
|
os.environ['CONDA_PREFIX'] = conda_environment
|
||||||
|
install(
|
||||||
|
C.CONFIG_FILE, store, hook_types=['pre-commit'],
|
||||||
|
hooks_activate_conda=True,
|
||||||
|
)
|
||||||
|
# No environment so pre-commit is not on the path when running!
|
||||||
|
# Should still pick up the python from when we installed
|
||||||
|
ret, output = _get_commit_output(
|
||||||
|
tempdir_factory,
|
||||||
|
env=get_environment_without_pre_commit(),
|
||||||
)
|
)
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
assert NORMAL_PRE_COMMIT_RUN.match(output)
|
assert NORMAL_PRE_COMMIT_RUN.match(output)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue