def test_autoupdate_old_revision_broken(tempdir_factory, in_tmpdir, store): """In $FUTURE_VERSION, hooks.yaml will no longer be supported. This asserts that when that day comes, pre-commit will be able to autoupdate despite not being able to read hooks.yaml in that repository. """ path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path, check=False) cmd_output('git', 'mv', C.MANIFEST_FILE, 'nope.yaml', cwd=path) git_commit(cwd=path) # Assume this is the revision the user's old repository was at rev = git.head_rev(path) cmd_output('git', 'mv', 'nope.yaml', C.MANIFEST_FILE, cwd=path) git_commit(cwd=path) update_rev = git.head_rev(path) config['rev'] = rev write_config('.', config) with open(C.CONFIG_FILE) as f: before = f.read() ret = autoupdate(C.CONFIG_FILE, store, tags_only=False) with open(C.CONFIG_FILE) as f: after = f.read() assert ret == 0 assert before != after assert update_rev in after
def test_additional_dependencies(tempdir_factory, store): path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) config['hooks'][0]['additional_dependencies'] = ['pep8'] repo = Repository.create(config, store) venv, = repo._venvs assert venv == (mock.ANY, 'python', 'default', ['pep8'])
def test_autoupdate_old_revision_broken( tempdir_factory, in_tmpdir, mock_out_store_directory, ): """In $FUTURE_VERSION, hooks.yaml will no longer be supported. This asserts that when that day comes, pre-commit will be able to autoupdate despite not being able to read hooks.yaml in that repository. """ path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path, check=False) with cwd(path): cmd_output('git', 'mv', C.MANIFEST_FILE, 'nope.yaml') cmd_output('git', 'commit', '-m', 'simulate old repo') # Assume this is the revision the user's old repository was at rev = get_head_sha(path) cmd_output('git', 'mv', 'nope.yaml', C.MANIFEST_FILE) cmd_output('git', 'commit', '-m', 'move hooks file') update_rev = get_head_sha(path) config['sha'] = rev write_config('.', config) before = open(C.CONFIG_FILE).read() ret = autoupdate(Runner('.', C.CONFIG_FILE), tags_only=False) after = open(C.CONFIG_FILE).read() assert ret == 0 assert before != after assert update_rev in after
def test_versions_ok(tempdir_factory, store, version): path = make_repo(tempdir_factory, 'script_hooks_repo') with modify_manifest(path) as manifest: manifest[0]['minimum_pre_commit_version'] = version config = make_config_from_repo(path) # Should succeed Repository.create(config, store).require_installed()
def test_control_c_control_c_on_install(tmpdir_factory, store): """Regression test for #186.""" path = make_repo(tmpdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) repo = Repository.create(config, store) hook = repo.hooks[0][1] class MyKeyboardInterrupt(KeyboardInterrupt): pass # To simulate a killed install, we'll make PythonEnv.run raise ^C # and then to simulate a second ^C during cleanup, we'll make shutil.rmtree # raise as well. with pytest.raises(MyKeyboardInterrupt): with mock.patch.object( PythonEnv, 'run', side_effect=MyKeyboardInterrupt, ): with mock.patch.object( shutil, 'rmtree', side_effect=MyKeyboardInterrupt, ): repo.run_hook(hook, []) # Should have made an environment, however this environment is broken! assert os.path.exists(repo.cmd_runner.path('py_env')) # However, it should be perfectly runnable (reinstall after botched # install) retv, stdout, stderr = repo.run_hook(hook, []) assert retv == 0
def test_default_python_language_version(store, tempdir_factory): path = make_repo(tempdir_factory, 'python_hooks_repo') repo_path = store.clone(path, get_head_sha(path)) manifest = Manifest(repo_path, path) # This assertion is difficult as it is version dependent, just assert # that it is *something* assert manifest.hooks['foo']['language_version'] != 'default'
def hook_disappearing_repo(tempdir_factory): path = make_repo(tempdir_factory, 'python_hooks_repo') original_rev = git.head_rev(path) with modify_manifest(path) as manifest: manifest[0]['id'] = 'bar' yield auto_namedtuple(path=path, original_rev=original_rev)
def test_additional_python_dependencies_installed(tempdir_factory, store): path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) config['hooks'][0]['additional_dependencies'] = ['mccabe'] repo = Repository.create(config, store) repo.require_installed() with python.in_env(repo._cmd_runner, 'default'): output = cmd_output('pip', 'freeze', '-l')[1] assert 'mccabe' in output
def test_additional_python_dependencies_installed(tempdir_factory, store): path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) config['hooks'][0]['additional_dependencies'] = ['mccabe'] repo = Repository.create(config, store) repo.run_hook(repo.hooks[0][1], []) with python.in_env(repo.cmd_runner, 'default') as env: output = env.run('pip freeze -l')[1] assert 'mccabe' in output
def test_config_overrides_repo_specifics(tmpdir_factory, store): path = make_repo(tmpdir_factory, 'script_hooks_repo') config = make_config_from_repo(path) repo = Repository.create(config, store) assert repo.hooks[0][1]['files'] == '' # Set the file regex to something else config['hooks'][0]['files'] = '\\.sh$' repo = Repository.create(config, store) assert repo.hooks[0][1]['files'] == '\\.sh$'
def _test_hook_repo( tempdir_factory, store, repo_path, hook_id, args, expected, expected_return_code=0, config_kwargs=None ): path = make_repo(tempdir_factory, repo_path) config = make_config_from_repo(path, **(config_kwargs or {})) repo = Repository.create(config, store) hook_dict = [hook for repo_hook_id, hook in repo.hooks if repo_hook_id == hook_id][0] ret = repo.run_hook(hook_dict, args) assert ret[0] == expected_return_code assert ret[1].replace(b"\r\n", b"\n") == expected
def test_config_overrides_repo_specifics(tempdir_factory, store): path = make_repo(tempdir_factory, "script_hooks_repo") config = make_config_from_repo(path) repo = Repository.create(config, store) assert repo.hooks[0][1]["files"] == "" # Set the file regex to something else config["hooks"][0]["files"] = "\\.sh$" repo = Repository.create(config, store) assert repo.hooks[0][1]["files"] == "\\.sh$"
def out_of_date_repo(tempdir_factory): path = make_repo(tempdir_factory, 'python_hooks_repo') original_rev = git.head_rev(path) git_commit(cwd=path) head_rev = git.head_rev(path) yield auto_namedtuple( path=path, original_rev=original_rev, head_rev=head_rev, )
def test_additional_dependencies_duplicated( tempdir_factory, store, log_warning_mock, ): path = make_repo(tempdir_factory, 'ruby_hooks_repo') config = make_config_from_repo(path) deps = ['thread_safe', 'tins', 'thread_safe'] config['hooks'][0]['additional_dependencies'] = deps repo = Repository.create(config, store) venv, = repo._venvs assert venv == (mock.ANY, 'ruby', 'default', ['thread_safe', 'tins'])
def test_really_long_file_paths(tmpdir_factory, store): base_path = tmpdir_factory.get() really_long_path = os.path.join(base_path, 'really_long' * 10) cmd_output('git', 'init', really_long_path) path = make_repo(tmpdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) with cwd(really_long_path): repo = Repository.create(config, store) repo.require_installed()
def test_really_long_file_paths(tempdir_factory, store): base_path = tempdir_factory.get() really_long_path = os.path.join(base_path, "really_long" * 10) cmd_output("git", "init", really_long_path) path = make_repo(tempdir_factory, "python_hooks_repo") config = make_config_from_repo(path) with cwd(really_long_path): repo = Repository.create(config, store) repo.require_installed()
def test_gc_repo_not_cloned(tempdir_factory, store, in_git_dir, cap_out): path = make_repo(tempdir_factory, 'script_hooks_repo') write_config('.', make_config_from_repo(path)) store.mark_config_used(C.CONFIG_FILE) assert _config_count(store) == 1 assert _repo_count(store) == 0 assert not gc(store) assert _config_count(store) == 1 assert _repo_count(store) == 0 assert cap_out.get().splitlines()[-1] == '0 repo(s) removed.'
def test_additional_ruby_dependencies_installed( tempdir_factory, store, ): # pragma: no cover (non-windows) path = make_repo(tempdir_factory, 'ruby_hooks_repo') config = make_config_from_repo(path) config['hooks'][0]['additional_dependencies'] = ['thread_safe'] repo = Repository.create(config, store) repo.run_hook(repo.hooks[0][1], []) with ruby.in_env(repo.cmd_runner, 'default') as env: output = env.run('gem list --local')[1] assert 'thread_safe' in output
def test_reinstall(tmpdir_factory, store): path = make_repo(tmpdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) repo = Repository.create(config, store) repo.require_installed() # Reinstall with same repo should not trigger another install # TODO: how to assert this? repo.require_installed() # Reinstall on another run should not trigger another install # TODO: how to assert this? repo = Repository.create(config, store) repo.require_installed()
def test_hook_id_not_present(tempdir_factory, store, fake_log_handler): path = make_repo(tempdir_factory, 'script_hooks_repo') config = make_config_from_repo(path) config['hooks'][0]['id'] = 'i-dont-exist' repo = Repository.create(config, store) with pytest.raises(SystemExit): repo.require_installed() assert fake_log_handler.handle.call_args[0][0].msg == ( '`i-dont-exist` is not present in repository {}. ' 'Typo? Perhaps it is introduced in a newer version? ' 'Often `pre-commit autoupdate` fixes this.'.format(path) )
def out_of_date_repo(tmpdir_factory): path = make_repo(tmpdir_factory, 'python_hooks_repo') original_sha = get_head_sha(path) # Make a commit with local.cwd(path): local['git']['commit', '--allow-empty', '-m', 'foo']() head_sha = get_head_sha(path) yield auto_namedtuple( path=path, original_sha=original_sha, head_sha=head_sha, )
def test_additional_ruby_dependencies_installed( tempdir_factory, store, ): # pragma: no cover (non-windows) path = make_repo(tempdir_factory, 'ruby_hooks_repo') config = make_config_from_repo(path) config['hooks'][0]['additional_dependencies'] = ['thread_safe', 'tins'] repo = Repository.create(config, store) repo.require_installed() with ruby.in_env(repo._cmd_runner, 'default'): output = cmd_output('gem', 'list', '--local')[1] assert 'thread_safe' in output assert 'tins' in output
def test_additional_node_dependencies_installed( tempdir_factory, store, ): # pragma: no cover (non-windows) path = make_repo(tempdir_factory, 'node_hooks_repo') config = make_config_from_repo(path) # Careful to choose a small package that's not depped by npm config['hooks'][0]['additional_dependencies'] = ['lodash'] repo = Repository.create(config, store) repo.run_hook(repo.hooks[0][1], []) with node.in_env(repo.cmd_runner, 'default') as env: env.run('npm config set global true') output = env.run(('npm ls'))[1] assert 'lodash' in output
def test_additional_node_dependencies_installed( tempdir_factory, store, ): # pragma: no cover (non-windows) path = make_repo(tempdir_factory, 'node_hooks_repo') config = make_config_from_repo(path) # Careful to choose a small package that's not depped by npm config['hooks'][0]['additional_dependencies'] = ['lodash'] repo = Repository.create(config, store) repo.require_installed() with node.in_env(repo._cmd_runner, 'default'): cmd_output('npm', 'config', 'set', 'global', 'true') output = cmd_output('npm', 'ls')[1] assert 'lodash' in output
def hook_disappearing_repo(tmpdir_factory): path = make_repo(tmpdir_factory, 'python_hooks_repo') original_sha = get_head_sha(path) with local.cwd(path): shutil.copy( get_resource_path('manifest_without_foo.yaml'), C.MANIFEST_FILE, ) local['git']('add', '.') local['git']('commit', '-m', 'Remove foo') yield auto_namedtuple(path=path, original_sha=original_sha)
def test_additional_dependencies_roll_forward(tempdir_factory, store): path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) # Run the repo once without additional_dependencies repo = Repository.create(config, store) repo.require_installed() # Now run it with additional_dependencies config['hooks'][0]['additional_dependencies'] = ['mccabe'] repo = Repository.create(config, store) repo.require_installed() # We should see our additional dependency installed with python.in_env(repo._cmd_runner, 'default'): output = cmd_output('pip', 'freeze', '-l')[1] assert 'mccabe' in output
def test_switch_language_versions_doesnt_clobber(tempdir_factory, store): # We're using the python3 repo because it prints the python version path = make_repo(tempdir_factory, "python3_hooks_repo") def run_on_version(version, expected_output): config = make_config_from_repo(path, hooks=[{"id": "python3-hook", "language_version": version}]) repo = Repository.create(config, store) hook_dict, = [hook for repo_hook_id, hook in repo.hooks if repo_hook_id == "python3-hook"] ret = repo.run_hook(hook_dict, []) assert ret[0] == 0 assert ret[1].replace(b"\r\n", b"\n") == expected_output run_on_version("python3.4", b"3.4\n[]\nHello World\n") run_on_version("python3.3", b"3.3\n[]\nHello World\n")
def test_too_new_version(tempdir_factory, store, fake_log_handler): path = make_repo(tempdir_factory, 'script_hooks_repo') with modify_manifest(path) as manifest: manifest[0]['minimum_pre_commit_version'] = '999.0.0' config = make_config_from_repo(path) repo = Repository.create(config, store) with pytest.raises(SystemExit): repo.require_installed() msg = fake_log_handler.handle.call_args[0][0].msg assert re.match( r'^The hook `bash_hook` requires pre-commit version 999\.0\.0 but ' r'version \d+\.\d+\.\d+ is installed. ' r'Perhaps run `pip install --upgrade pre-commit`\.$', msg, )
def test_legacy_manifest_warn(store, tempdir_factory, log_warning_mock): path = make_repo(tempdir_factory, 'legacy_hooks_yaml_repo') head_sha = get_head_sha(path) repo_path = store.clone(path, head_sha) Manifest(repo_path, path).manifest_contents # Should have printed a warning assert log_warning_mock.call_args_list[0][0][0] == ( '{} uses legacy hooks.yaml to provide hooks.\n' 'In newer versions, this file is called .pre-commit-hooks.yaml\n' 'This will work in this version of pre-commit but will be removed at ' 'a later time.\n' 'If `pre-commit autoupdate` does not silence this warning consider ' 'making an issue / pull request.'.format(path) )
def test_reinstall(tmpdir_factory, store, log_info_mock): path = make_repo(tmpdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) repo = Repository.create(config, store) repo.require_installed() # We print some logging during clone (1) + install (3) assert log_info_mock.call_count == 4 log_info_mock.reset_mock() # Reinstall with same repo should not trigger another install repo.require_installed() assert log_info_mock.call_count == 0 # Reinstall on another run should not trigger another install repo = Repository.create(config, store) repo.require_installed() assert log_info_mock.call_count == 0
def test_reinstall(tempdir_factory, store, log_info_mock): path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) repo = Repository.create(config, store) repo.require_installed() # We print some logging during clone (1) + install (3) assert log_info_mock.call_count == 4 log_info_mock.reset_mock() # Reinstall with same repo should not trigger another install repo.require_installed() assert log_info_mock.call_count == 0 # Reinstall on another run should not trigger another install repo = Repository.create(config, store) repo.require_installed() assert log_info_mock.call_count == 0
def test_additional_golang_dependencies_installed( tempdir_factory, store, ): path = make_repo(tempdir_factory, 'golang_hooks_repo') config = make_config_from_repo(path) # A small go package deps = ['github.com/golang/example/hello'] config['hooks'][0]['additional_dependencies'] = deps repo = Repository.create(config, store) repo.require_installed() binaries = os.listdir(repo._cmd_runner.path( helpers.environment_dir(golang.ENVIRONMENT_DIR, 'default'), 'bin', )) # normalize for windows binaries = [os.path.splitext(binary)[0] for binary in binaries] assert 'hello' in binaries
def test_additional_rust_cli_dependencies_installed( tempdir_factory, store, dep, ): path = make_repo(tempdir_factory, 'rust_hooks_repo') config = make_config_from_repo(path) # A small rust package with no dependencies. config['hooks'][0]['additional_dependencies'] = [dep] repo = Repository.create(config, store) repo.require_installed() (prefix, _, _, _), = repo._venvs() binaries = os.listdir(prefix.path( helpers.environment_dir(rust.ENVIRONMENT_DIR, 'default'), 'bin', )) # normalize for windows binaries = [os.path.splitext(binary)[0] for binary in binaries] assert 'shellharden' in binaries
def _test_hook_repo( tempdir_factory, store, repo_path, hook_id, args, expected, expected_return_code=0, config_kwargs=None, color=False, ): path = make_repo(tempdir_factory, repo_path) config = make_config_from_repo(path, **(config_kwargs or {})) ret, out = _get_hook(config, store, hook_id).run(args, color=color) assert ret == expected_return_code assert _norm_out(out) == expected
def test_additional_rust_cli_dependencies_installed( tempdir_factory, store, dep, ): path = make_repo(tempdir_factory, 'rust_hooks_repo') config = make_config_from_repo(path) # A small rust package with no dependencies. config['hooks'][0]['additional_dependencies'] = [dep] hook = _get_hook(config, store, 'rust-hook') binaries = os.listdir( hook.prefix.path( helpers.environment_dir(rust.ENVIRONMENT_DIR, C.DEFAULT), 'bin', ), ) # normalize for windows binaries = [os.path.splitext(binary)[0] for binary in binaries] assert 'shellharden' in binaries
def _test_hook_repo(tmpdir_factory, store, repo_path, hook_id, args, expected, expected_return_code=0, config_kwargs=None): path = make_repo(tmpdir_factory, repo_path) config = make_config_from_repo(path, **(config_kwargs or {})) repo = Repository.create(config, store) hook_dict = [ hook for repo_hook_id, hook in repo.hooks if repo_hook_id == hook_id ][0] ret = repo.run_hook(hook_dict, args) assert ret[0] == expected_return_code assert ret[1].replace('\r\n', '\n') == expected
def test_additional_golang_dependencies_installed( tempdir_factory, store, ): path = make_repo(tempdir_factory, 'golang_hooks_repo') config = make_config_from_repo(path) # A small go package deps = ['github.com/golang/example/hello'] config['hooks'][0]['additional_dependencies'] = deps hook = _get_hook(config, store, 'golang-hook') binaries = os.listdir( hook.prefix.path( helpers.environment_dir(golang.ENVIRONMENT_DIR, C.DEFAULT), 'bin', ), ) # normalize for windows binaries = [os.path.splitext(binary)[0] for binary in binaries] assert 'hello' in binaries
def test_additional_dependencies_roll_forward(tempdir_factory, store): path = make_repo(tempdir_factory, 'python_hooks_repo') config1 = make_config_from_repo(path) hook1 = _get_hook(config1, store, 'foo') with python.in_env(hook1.prefix, hook1.language_version): assert 'mccabe' not in cmd_output('pip', 'freeze', '-l')[1] # Make another repo with additional dependencies config2 = make_config_from_repo(path) config2['hooks'][0]['additional_dependencies'] = ['mccabe'] hook2 = _get_hook(config2, store, 'foo') with python.in_env(hook2.prefix, hook2.language_version): assert 'mccabe' in cmd_output('pip', 'freeze', '-l')[1] # should not have affected original with python.in_env(hook1.prefix, hook1.language_version): assert 'mccabe' not in cmd_output('pip', 'freeze', '-l')[1]
def test_invalid_manifest_gcd(tempdir_factory, store, in_git_dir, cap_out): # clean up repos from old pre-commit versions path = make_repo(tempdir_factory, 'script_hooks_repo') write_config('.', make_config_from_repo(path)) store.mark_config_used(C.CONFIG_FILE) # trigger a clone assert not autoupdate(C.CONFIG_FILE, store, tags_only=False) # we'll "break" the manifest to simulate an old version clone (_, _, path), = store.select_all_repos() os.remove(os.path.join(path, C.MANIFEST_FILE)) assert _config_count(store) == 1 assert _repo_count(store) == 1 assert not gc(store) assert _config_count(store) == 1 assert _repo_count(store) == 0 assert cap_out.get().splitlines()[-1] == '1 repo(s) removed.'
def test_gc(tempdir_factory, store, in_git_dir, cap_out): path = make_repo(tempdir_factory, 'script_hooks_repo') old_rev = git.head_rev(path) git_commit(cwd=path) write_config('.', make_config_from_repo(path, rev=old_rev)) store.mark_config_used(C.CONFIG_FILE) # update will clone both the old and new repo, making the old one gc-able assert not autoupdate(C.CONFIG_FILE, store, tags_only=False) assert _config_count(store) == 1 assert _repo_count(store) == 2 assert not gc(store) assert _config_count(store) == 1 assert _repo_count(store) == 1 assert cap_out.get().splitlines()[-1] == '1 repo(s) removed.' _remove_config_assert_cleared(store, cap_out)
def test_manifest_hooks(tempdir_factory, store): path = make_repo(tempdir_factory, 'script_hooks_repo') config = make_config_from_repo(path) hook = _get_hook(config, store, 'bash_hook') assert hook == Hook( src=f'file://{path}', prefix=Prefix(mock.ANY), additional_dependencies=[], alias='', always_run=False, args=[], description='', entry='bin/hook.sh', exclude='^$', exclude_types=[], files='', id='bash_hook', language='script', language_version='default', log_file='', minimum_pre_commit_version='0', name='Bash hook', pass_filenames=True, require_serial=False, stages=( 'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg', 'post-commit', 'manual', 'post-checkout', 'push', 'post-merge', 'post-rewrite', ), types=['file'], types_or=[], verbose=False, fail_fast=False, )
def test_switch_language_versions_doesnt_clobber(tempdir_factory, store): # We're using the python3 repo because it prints the python version path = make_repo(tempdir_factory, 'python3_hooks_repo') def run_on_version(version, expected_output): config = make_config_from_repo( path, hooks=[{'id': 'python3-hook', 'language_version': version}], ) repo = Repository.create(config, store) hook_dict, = [ hook for repo_hook_id, hook in repo.hooks if repo_hook_id == 'python3-hook' ] ret = repo.run_hook(hook_dict, []) assert ret[0] == 0 assert _norm_out(ret[1]) == expected_output run_on_version('python3.4', b'3.4\n[]\nHello World\n') run_on_version('python3.5', b'3.5\n[]\nHello World\n')
def test_invalidated_virtualenv(tempdir_factory, store): # A cached virtualenv may become invalidated if the system python upgrades # This should not cause every hook in that virtualenv to fail. path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) hook = _get_hook(config, store, 'foo') # Simulate breaking of the virtualenv libdir = hook.prefix.path( helpers.environment_dir(python.ENVIRONMENT_DIR, hook.language_version), 'lib', hook.language_version, ) paths = [ os.path.join(libdir, p) for p in ('site.py', 'site.pyc', '__pycache__') ] cmd_output_b('rm', '-rf', *paths) # pre-commit should rebuild the virtualenv and it should be runnable retv, stdout, stderr = _get_hook(config, store, 'foo').run(()) assert retv == 0
def test_gc_config_with_missing_hook( tempdir_factory, store, in_git_dir, cap_out, ): path = make_repo(tempdir_factory, 'script_hooks_repo') write_config('.', make_config_from_repo(path)) store.mark_config_used(C.CONFIG_FILE) # to trigger a clone all_hooks(load_config(C.CONFIG_FILE), store) with modify_config() as config: # add a hook which does not exist, make sure we don't crash config['repos'][0]['hooks'].append({'id': 'does-not-exist'}) assert _config_count(store) == 1 assert _repo_count(store) == 1 assert not gc(store) assert _config_count(store) == 1 assert _repo_count(store) == 1 assert cap_out.get().splitlines()[-1] == '0 repo(s) removed.' _remove_config_assert_cleared(store, cap_out)
def test_invalidated_virtualenv(tempdir_factory, store): # A cached virtualenv may become invalidated if the system python upgrades # This should not cause every hook in that virtualenv to fail. path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) repo = Repository.create(config, store) # Simulate breaking of the virtualenv repo.require_installed() version = python.get_default_version() libdir = repo._cmd_runner.path('py_env-{}'.format(version), 'lib', version) paths = [ os.path.join(libdir, p) for p in ('site.py', 'site.pyc', '__pycache__') ] cmd_output('rm', '-rf', *paths) # pre-commit should rebuild the virtualenv and it should be runnable repo = Repository.create(config, store) hook = repo.hooks[0][1] retv, stdout, stderr = repo.run_hook(hook, []) assert retv == 0
def test_try_repo_uncommitted_changes(cap_out, tempdir_factory): repo = make_repo(tempdir_factory, 'script_hooks_repo') # make an uncommitted change with modify_manifest(repo, commit=False) as manifest: manifest[0]['name'] = 'modified name!' with cwd(git_dir(tempdir_factory)): open('test-fie', 'a').close() cmd_output('git', 'add', '.') assert not try_repo(try_repo_opts(repo)) start, config, rest = _get_out(cap_out) assert start == '[WARNING] Creating temporary repo with uncommitted changes...\n' # noqa: E501 config_pattern = re_assert.Matches( '^repos:\n' '- repo: .+shadow-repo\n' ' rev: .+\n' ' hooks:\n' ' - id: bash_hook\n$', ) config_pattern.assert_matches(config) assert rest == 'modified name!...........................................................Passed\n' # noqa: E501
def test_additional_dependencies_roll_forward(tempdir_factory, store): path = make_repo(tempdir_factory, 'python_hooks_repo') config1 = make_config_from_repo(path) repo1 = Repository.create(config1, store) repo1.require_installed() (prefix1, _, version1, _), = repo1._venvs() with python.in_env(prefix1, version1): assert 'mccabe' not in cmd_output('pip', 'freeze', '-l')[1] # Make another repo with additional dependencies config2 = make_config_from_repo(path) config2['hooks'][0]['additional_dependencies'] = ['mccabe'] repo2 = Repository.create(config2, store) repo2.require_installed() (prefix2, _, version2, _), = repo2._venvs() with python.in_env(prefix2, version2): assert 'mccabe' in cmd_output('pip', 'freeze', '-l')[1] # should not have affected original with python.in_env(prefix1, version1): assert 'mccabe' not in cmd_output('pip', 'freeze', '-l')[1]
def test_control_c_control_c_on_install(tempdir_factory, store): """Regression test for #186.""" path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) hooks = [_get_hook_no_install(config, store, 'foo')] class MyKeyboardInterrupt(KeyboardInterrupt): pass # To simulate a killed install, we'll make PythonEnv.run raise ^C # and then to simulate a second ^C during cleanup, we'll make shutil.rmtree # raise as well. with pytest.raises(MyKeyboardInterrupt): with mock.patch.object( helpers, 'run_setup_cmd', side_effect=MyKeyboardInterrupt, ): with mock.patch.object( shutil, 'rmtree', side_effect=MyKeyboardInterrupt, ): install_hook_envs(hooks, store) # Should have made an environment, however this environment is broken! hook, = hooks assert hook.prefix.exists( helpers.environment_dir(python.ENVIRONMENT_DIR, hook.language_version), ) # However, it should be perfectly runnable (reinstall after botched # install) install_hook_envs(hooks, store) ret, out = _hook_run(hook, (), color=False) assert ret == 0
def test_autoupdate_old_revision_broken(tempdir_factory, in_tmpdir, store): """In $FUTURE_VERSION, hooks.yaml will no longer be supported. This asserts that when that day comes, pre-commit will be able to autoupdate despite not being able to read hooks.yaml in that repository. """ path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path, check=False) cmd_output('git', 'mv', C.MANIFEST_FILE, 'nope.yaml', cwd=path) cmd_output('git', 'commit', '-m', 'simulate old repo', cwd=path) # Assume this is the revision the user's old repository was at rev = git.head_rev(path) cmd_output('git', 'mv', 'nope.yaml', C.MANIFEST_FILE, cwd=path) cmd_output('git', 'commit', '-m', 'move hooks file', cwd=path) update_rev = git.head_rev(path) config['rev'] = rev write_config('.', config) before = open(C.CONFIG_FILE).read() ret = autoupdate(Runner('.', C.CONFIG_FILE), store, tags_only=False) after = open(C.CONFIG_FILE).read() assert ret == 0 assert before != after assert update_rev in after
def test_manifest_hooks(tempdir_factory, store): path = make_repo(tempdir_factory, 'script_hooks_repo') config = make_config_from_repo(path) repo = Repository.create(config, store) assert repo.manifest_hooks['bash_hook'] == { 'always_run': False, 'additional_dependencies': [], 'args': [], 'description': '', 'entry': 'bin/hook.sh', 'exclude': '^$', 'files': '', 'id': 'bash_hook', 'language': 'script', 'language_version': 'default', 'log_file': '', 'minimum_pre_commit_version': '0', 'name': 'Bash hook', 'pass_filenames': True, 'stages': [], 'types': ['file'], 'exclude_types': [], }
def up_to_date(tempdir_factory): yield make_repo(tempdir_factory, 'python_hooks_repo')
def _create_repo_with_tags(tempdir_factory, src, tag): path = make_repo(tempdir_factory, src) with cwd(path): cmd_output('git', 'tag', tag) return path
def test_languages(tempdir_factory, store): path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) repo = Repository.create(config, store) assert repo.languages == set([('python', 'default')])
def manifest(store, tempdir_factory): path = make_repo(tempdir_factory, 'script_hooks_repo') head_sha = get_head_sha(path) repo_path = store.clone(path, head_sha) yield Manifest(repo_path, path)
def test_venvs(tempdir_factory, store): path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) repo = Repository.create(config, store) venv, = repo._venvs assert venv == (mock.ANY, 'python', python.get_default_version(), [])
def test_additional_dependencies(tempdir_factory, store): path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) config['hooks'][0]['additional_dependencies'] = ['pep8'] repo = Repository.create(config, store) assert repo.additional_dependencies['python']['default'] == set(('pep8',))
def _run_try_repo(tempdir_factory, **kwargs): repo = make_repo(tempdir_factory, 'modified_file_returns_zero_repo') with cwd(git_dir(tempdir_factory)): _add_test_file() assert not try_repo(try_repo_opts(repo, **kwargs))
def _run_try_repo(tempdir_factory, **kwargs): repo = make_repo(tempdir_factory, 'modified_file_returns_zero_repo') with cwd(git_dir(tempdir_factory)): open('test-file', 'a').close() cmd_output('git', 'add', '.') assert not try_repo(try_repo_opts(repo, **kwargs))
def _create_repo_with_tags(tempdir_factory, src, tag): path = make_repo(tempdir_factory, src) cmd_output_b('git', 'tag', tag, cwd=path) return path