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 _update_repository(repo_config, runner): """Updates a repository to the tip of `master`. If the repository cannot be updated because a hook that is configured does not exist in `master`, this raises a RepositoryCannotBeUpdatedError Args: repo_config - A config for a repository """ repo = Repository.create(repo_config, runner.store) with cwd(repo.repo_path_getter.repo_path): cmd_output('git', 'fetch') head_sha = cmd_output('git', 'rev-parse', 'origin/master')[1].strip() # Don't bother trying to update if our sha is the same if head_sha == repo_config['sha']: return repo_config # Construct a new config with the head sha new_config = OrderedDict(repo_config) new_config['sha'] = head_sha new_repo = Repository.create(new_config, runner.store) # See if any of our hooks were deleted with the new commits hooks = set(hook_id for hook_id, _ in repo.hooks) hooks_missing = hooks - (hooks & set(new_repo.manifest.hooks.keys())) if hooks_missing: raise RepositoryCannotBeUpdatedError( 'Cannot update because the tip of master is missing these hooks:\n' '{0}'.format(', '.join(sorted(hooks_missing))) ) return new_config
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_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 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_tags_on_repositories(in_tmpdir, tempdir_factory, store): tag = "v1.1" git_dir_1 = _create_repo_with_tags(tempdir_factory, "prints_cwd_repo", tag) git_dir_2 = _create_repo_with_tags(tempdir_factory, "script_hooks_repo", tag) repo_1 = Repository.create(make_config_from_repo(git_dir_1, sha=tag), store) ret = repo_1.run_hook(repo_1.hooks[0][1], ["-L"]) assert ret[0] == 0 assert ret[1].strip() == _norm_pwd(in_tmpdir) repo_2 = Repository.create(make_config_from_repo(git_dir_2, sha=tag), store) ret = repo_2.run_hook(repo_2.hooks[0][1], ["bar"]) assert ret[0] == 0 assert ret[1] == b"bar\nHello World\n"
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_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 repositories(self): """Returns a tuple of the configured repositories.""" repos = self.config['repos'] repos = tuple(Repository.create(x, self.store) for x in repos) for repo in repos: repo.require_installed() return repos
def repositories(self): """Returns a tuple of the configured repositories.""" config = load_config(self.config_file_path) repositories = tuple(Repository.create(x, self.store) for x in config) for repository in repositories: repository.require_installed() return repositories
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
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_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_local_repository(): config = config_with_local_hooks() local_repo = Repository.create(config, 'dummy') with pytest.raises(NotImplementedError): local_repo.sha with pytest.raises(NotImplementedError): local_repo.manifest assert len(local_repo.hooks) == 1
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_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_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_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_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_tags_on_repositories(in_tmpdir, tmpdir_factory, store): tag = 'v1.1' git_dir_1 = _create_repo_with_tags(tmpdir_factory, 'prints_cwd_repo', tag) git_dir_2 = _create_repo_with_tags( tmpdir_factory, 'script_hooks_repo', tag, ) repo_1 = Repository.create( make_config_from_repo(git_dir_1, sha=tag), store, ) ret = repo_1.run_hook(repo_1.hooks[0][1], ['-L']) assert ret[0] == 0 assert ret[1].strip() == _norm_pwd(in_tmpdir) repo_2 = Repository.create( make_config_from_repo(git_dir_2, sha=tag), store, ) ret = repo_2.run_hook(repo_2.hooks[0][1], ['bar']) assert ret[0] == 0 assert ret[1] == 'bar\nHello World\n'
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_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 _update_repo(repo_config, runner, tags_only): """Updates a repository to the tip of `master`. If the repository cannot be updated because a hook that is configured does not exist in `master`, this raises a RepositoryCannotBeUpdatedError Args: repo_config - A config for a repository """ repo = Repository.create(repo_config, runner.store) with cwd(repo._repo_path): cmd_output('git', 'fetch') tag_cmd = ('git', 'describe', 'origin/master', '--tags') if tags_only: tag_cmd += ('--abbrev=0',) else: tag_cmd += ('--exact',) try: rev = cmd_output(*tag_cmd)[1].strip() except CalledProcessError: rev = cmd_output('git', 'rev-parse', 'origin/master')[1].strip() # Don't bother trying to update if our sha is the same if rev == repo_config['sha']: return repo_config # Construct a new config with the head sha new_config = OrderedDict(repo_config) new_config['sha'] = rev new_repo = Repository.create(new_config, runner.store) # See if any of our hooks were deleted with the new commits hooks = {hook['id'] for hook in repo.repo_config['hooks']} hooks_missing = hooks - (hooks & set(new_repo.manifest.hooks)) if hooks_missing: raise RepositoryCannotBeUpdatedError( 'Cannot update because the tip of master is missing these hooks:\n' '{}'.format(', '.join(sorted(hooks_missing))) ) return new_config
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_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 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_local_python_repo(store): # Make a "local" hooks repo that just installs our other hooks repo repo_path = get_resource_path('python_hooks_repo') manifest = load_manifest(os.path.join(repo_path, C.MANIFEST_FILE)) hooks = [ dict(hook, additional_dependencies=[repo_path]) for hook in manifest ] config = {'repo': 'local', 'hooks': hooks} repo = Repository.create(config, store) (_, hook), = repo.hooks ret = repo.run_hook(hook, ('filename',)) assert ret[0] == 0 assert ret[1].replace(b'\r\n', b'\n') == b"['filename']\nHello World\n"
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 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 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
def test_local_repository(): config = config_with_local_hooks() local_repo = Repository.create(config, 'dummy') with pytest.raises(NotImplementedError): local_repo.manifest assert len(local_repo.hooks) == 1
def test_repo_url(mock_repo_config): repo = Repository(mock_repo_config, None) assert repo.repo_url == '[email protected]:pre-commit/pre-commit-hooks'
def test_sha(mock_repo_config): repo = Repository(mock_repo_config, None) assert repo.sha == '5e713f8878b7d100c0e059f8cc34be4fc2e8f897'
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 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')])