def test_clone_shallow_failure_fallback_to_complete( store, tempdir_factory, log_info_mock, ): path = git_dir(tempdir_factory) with cwd(path): git_commit() rev = git.head_rev(path) git_commit() # Force shallow clone failure def fake_shallow_clone(self, *args, **kwargs): raise CalledProcessError(None, None, None) store._shallow_clone = fake_shallow_clone ret = store.clone(path, rev) # Should have printed some stuff assert log_info_mock.call_args_list[0][0][0].startswith( 'Initializing environment for ', ) # Should return a directory inside of the store assert os.path.exists(ret) assert ret.startswith(store.directory) # Directory should start with `repo` _, dirname = os.path.split(ret) assert dirname.startswith('repo') # Should be checked out to the rev we specified assert git.head_rev(ret) == rev # Assert there's an entry in the sqlite db for this assert store.select_all_repos() == [(path, rev, ret)]
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() assert autoupdate(C.CONFIG_FILE, store, freeze=False, tags_only=False) == 0 with open(C.CONFIG_FILE) as f: after = f.read() assert before != after assert update_rev in after
def test_autoupdate_only_one_to_update(up_to_date, out_of_date, tmpdir, store): fmt = ('repos:\n' '- repo: {}\n' ' rev: {}\n' ' hooks:\n' ' - id: foo\n' '- repo: {}\n' ' rev: {}\n' ' hooks:\n' ' - id: foo\n') cfg = tmpdir.join(C.CONFIG_FILE) before = fmt.format( up_to_date, git.head_rev(up_to_date), out_of_date.path, out_of_date.original_rev, ) cfg.write(before) assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 assert cfg.read() == fmt.format( up_to_date, git.head_rev(up_to_date), out_of_date.path, out_of_date.head_rev, )
def test_clone(store, tempdir_factory, log_info_mock): path = git_dir(tempdir_factory) with cwd(path): cmd_output('git', 'commit', '--allow-empty', '-m', 'foo') rev = git.head_rev(path) cmd_output('git', 'commit', '--allow-empty', '-m', 'bar') ret = store.clone(path, rev) # Should have printed some stuff assert log_info_mock.call_args_list[0][0][0].startswith( 'Initializing environment for ', ) # Should return a directory inside of the store assert os.path.exists(ret) assert ret.startswith(store.directory) # Directory should start with `repo` _, dirname = os.path.split(ret) assert dirname.startswith('repo') # Should be checked out to the rev we specified assert git.head_rev(ret) == rev # Assert there's an entry in the sqlite db for this with sqlite3.connect(store.db_path) as db: path, = db.execute( 'SELECT path from repos WHERE repo = ? and ref = ?', (path, rev), ).fetchone() assert path == ret
def _repo_ref(tmpdir: str, repo: str, ref: Optional[str]) -> Tuple[str, str]: # if `ref` is explicitly passed, use it if ref is not None: return repo, ref ref = git.head_rev(repo) # if it exists on disk, we'll try and clone it with the local changes if os.path.exists(repo) and git.has_diff("HEAD", repo=repo): logger.warning("Creating temporary repo with uncommitted changes...") shadow = os.path.join(tmpdir, "shadow-repo") cmd_output_b("git", "clone", repo, shadow) cmd_output_b("git", "checkout", ref, "-b", "_pc_tmp", cwd=shadow) idx = git.git_path("index", repo=shadow) objs = git.git_path("objects", repo=shadow) env = dict(os.environ, GIT_INDEX_FILE=idx, GIT_OBJECT_DIRECTORY=objs) staged_files = git.get_staged_files(cwd=repo) if staged_files: xargs(("git", "add", "--"), staged_files, cwd=repo, env=env) cmd_output_b("git", "add", "-u", cwd=repo, env=env) git.commit(repo=shadow) return shadow, git.head_rev(shadow) else: return repo, ref
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 _repo_ref(tmpdir, repo, ref): # if `ref` is explicitly passed, use it if ref: return repo, ref ref = git.head_rev(repo) # if it exists on disk, we'll try and clone it with the local changes if os.path.exists(repo) and git.has_diff('HEAD', repo=repo): logger.warning('Creating temporary repo with uncommitted changes...') shadow = os.path.join(tmpdir, 'shadow-repo') cmd_output_b('git', 'clone', repo, shadow) cmd_output_b('git', 'checkout', ref, '-b', '_pc_tmp', cwd=shadow) idx = git.git_path('index', repo=shadow) objs = git.git_path('objects', repo=shadow) env = dict(os.environ, GIT_INDEX_FILE=idx, GIT_OBJECT_DIRECTORY=objs) staged_files = git.get_staged_files(cwd=repo) if staged_files: xargs(('git', 'add', '--'), staged_files, cwd=repo, env=env) cmd_output_b('git', 'add', '-u', cwd=repo, env=env) git.commit(repo=shadow) return shadow, git.head_rev(shadow) else: return repo, ref
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) cmd_output('git', '-C', path, 'mv', C.MANIFEST_FILE, 'nope.yaml') cmd_output('git', '-C', path, 'commit', '-m', 'simulate old repo') # Assume this is the revision the user's old repository was at rev = git.head_rev(path) cmd_output('git', '-C', path, 'mv', 'nope.yaml', C.MANIFEST_FILE) cmd_output('git', '-C', path, 'commit', '-m', 'move hooks file') update_rev = git.head_rev(path) config['rev'] = 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 _repo_ref(tmpdir, repo, ref): # if `ref` is explicitly passed, use it if ref: return repo, ref ref = git.head_rev(repo) # if it exists on disk, we'll try and clone it with the local changes if os.path.exists(repo) and git.has_diff('HEAD', repo=repo): logger.warning('Creating temporary repo with uncommitted changes...') shadow = os.path.join(tmpdir, 'shadow-repo') cmd_output('git', 'clone', repo, shadow) cmd_output('git', 'checkout', ref, '-b', '_pc_tmp', cwd=shadow) idx = git.git_path('index', repo=shadow) objs = git.git_path('objects', repo=shadow) env = dict(os.environ, GIT_INDEX_FILE=idx, GIT_OBJECT_DIRECTORY=objs) staged_files = git.get_staged_files(cwd=repo) if staged_files: xargs(('git', 'add', '--'), staged_files, cwd=repo, env=env) cmd_output('git', 'add', '-u', cwd=repo, env=env) git.commit(repo=shadow) return shadow, git.head_rev(shadow) else: return repo, ref
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 push_example(tempdir_factory): src = git_dir(tempdir_factory) git_commit(cwd=src) src_head = git.head_rev(src) clone = tempdir_factory.get() cmd_output('git', 'clone', src, clone) git_commit(cwd=clone) clone_head = git.head_rev(clone) return (src, src_head, clone, clone_head)
def out_of_date_repo(tempdir_factory): path = make_repo(tempdir_factory, 'python_hooks_repo') original_rev = git.head_rev(path) # Make a commit cmd_output('git', 'commit', '--allow-empty', '-m', 'foo', cwd=path) head_rev = git.head_rev(path) yield auto_namedtuple( path=path, original_rev=original_rev, head_rev=head_rev, )
def test_make_archive(in_git_dir, tmpdir): output_dir = tmpdir.join('output').ensure_dir() # Add a files to the git directory in_git_dir.join('foo').ensure() cmd_output('git', 'add', '.') git_commit() # We'll use this rev head_rev = git.head_rev('.') # And check that this file doesn't exist in_git_dir.join('bar').ensure() cmd_output('git', 'add', '.') git_commit() # Do the thing archive_path = make_archives.make_archive( 'foo', in_git_dir.strpath, head_rev, output_dir.strpath, ) expected = output_dir.join('foo.tar.gz') assert archive_path == expected.strpath assert expected.exists() extract_dir = tmpdir.join('extract').ensure_dir() with tarfile.open(archive_path) as tf: tf.extractall(extract_dir.strpath) # Verify the contents of the tar assert extract_dir.join('foo').isdir() assert extract_dir.join('foo/foo').exists() assert not extract_dir.join('foo/.git').exists() assert not extract_dir.join('foo/bar').exists()
def test_make_archive(tempdir_factory): output_dir = tempdir_factory.get() git_path = git_dir(tempdir_factory) # Add a files to the git directory open(os.path.join(git_path, 'foo'), 'a').close() cmd_output('git', 'add', '.', cwd=git_path) cmd_output('git', 'commit', '-m', 'foo', cwd=git_path) # We'll use this rev head_rev = git.head_rev(git_path) # And check that this file doesn't exist open(os.path.join(git_path, 'bar'), 'a').close() cmd_output('git', 'add', '.', cwd=git_path) cmd_output('git', 'commit', '-m', 'bar', cwd=git_path) # Do the thing archive_path = make_archives.make_archive( 'foo', git_path, head_rev, output_dir, ) assert archive_path == os.path.join(output_dir, 'foo.tar.gz') assert os.path.exists(archive_path) extract_dir = tempdir_factory.get() # Extract the tar with tarfile.open(archive_path) as tf: tf.extractall(extract_dir) # Verify the contents of the tar assert os.path.exists(os.path.join(extract_dir, 'foo')) assert os.path.exists(os.path.join(extract_dir, 'foo', 'foo')) assert not os.path.exists(os.path.join(extract_dir, 'foo', '.git')) assert not os.path.exists(os.path.join(extract_dir, 'foo', 'bar'))
def test_post_checkout_integration(tempdir_factory, store): path = git_dir(tempdir_factory) config = [ { 'repo': 'local', 'hooks': [{ 'id': 'post-checkout', 'name': 'Post checkout', 'entry': 'bash -c "echo ${PRE_COMMIT_TO_REF}"', 'language': 'system', 'always_run': True, 'verbose': True, 'stages': ['post-checkout'], }], }, {'repo': 'meta', 'hooks': [{'id': 'identity'}]}, ] write_config(path, config) with cwd(path): cmd_output('git', 'add', '.') git_commit() # add a file only on `feature`, it should not be passed to hooks cmd_output('git', 'checkout', '-b', 'feature') open('some_file', 'a').close() cmd_output('git', 'add', '.') git_commit() cmd_output('git', 'checkout', 'master') install(C.CONFIG_FILE, store, hook_types=['post-checkout']) retc, _, stderr = cmd_output('git', 'checkout', 'feature') assert stderr is not None assert retc == 0 assert git.head_rev(path) in stderr assert 'some_file' not in stderr
def try_repo(args): ref = args.ref or git.head_rev(args.repo) with tmpdir() as tempdir: if args.hook: hooks = [{'id': args.hook}] else: repo_path = Store(tempdir).clone(args.repo, ref) manifest = load_manifest(os.path.join(repo_path, C.MANIFEST_FILE)) manifest = sorted(manifest, key=lambda hook: hook['id']) hooks = [{'id': hook['id']} for hook in manifest] items = (('repo', args.repo), ('rev', ref), ('hooks', hooks)) config = {'repos': [collections.OrderedDict(items)]} config_s = ordered_dump(config, **C.YAML_DUMP_KWARGS) config_filename = os.path.join(tempdir, C.CONFIG_FILE) with open(config_filename, 'w') as cfg: cfg.write(config_s) output.write_line('=' * 79) output.write_line('Using config:') output.write_line('=' * 79) output.write(config_s) output.write_line('=' * 79) runner = Runner('.', config_filename, store_dir=tempdir) return run(runner, args)
def hook_disappearing(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 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_try_repo_specific_revision(cap_out, tempdir_factory): repo = make_repo(tempdir_factory, 'script_hooks_repo') ref = git.head_rev(repo) git_commit(cwd=repo) with cwd(git_dir(tempdir_factory)): _add_test_file() assert not try_repo(try_repo_opts(repo, ref=ref)) _, config, _ = _get_out(cap_out) assert ref in config
def hook_disappearing_repo(tempdir_factory): path = make_repo(tempdir_factory, 'python_hooks_repo') original_rev = git.head_rev(path) shutil.copy( get_resource_path('manifest_without_foo.yaml'), os.path.join(path, C.MANIFEST_FILE), ) cmd_output('git', 'add', '.', cwd=path) cmd_output('git', 'commit', '-m', 'Remove foo', cwd=path) yield auto_namedtuple(path=path, original_rev=original_rev)
def test_autoupdate_up_to_date_repo(up_to_date, tmpdir, store): contents = ( 'repos:\n' '- repo: {}\n' ' rev: {}\n' ' hooks:\n' ' - id: foo\n' ).format(up_to_date, git.head_rev(up_to_date)) cfg = tmpdir.join(C.CONFIG_FILE) cfg.write(contents) assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 assert cfg.read() == contents
def test_clone(store, tempdir_factory, log_info_mock): path = git_dir(tempdir_factory) with cwd(path): git_commit() rev = git.head_rev(path) git_commit() ret = store.clone(path, rev) # Should have printed some stuff assert log_info_mock.call_args_list[0][0][0].startswith( 'Initializing environment for ', ) # Should return a directory inside of the store assert os.path.exists(ret) assert ret.startswith(store.directory) # Directory should start with `repo` _, dirname = os.path.split(ret) assert dirname.startswith('repo') # Should be checked out to the rev we specified assert git.head_rev(ret) == rev # Assert there's an entry in the sqlite db for this assert store.select_all_repos() == [(path, rev, ret)]
def test_pushing_orphan_branch(push_example): src, src_head, clone, _ = push_example cmd_output('git', 'checkout', '--orphan', 'b2', cwd=clone) git_commit(cwd=clone, msg='something else to get unique hash') clone_rev = git.head_rev(clone) with cwd(clone): args = ('origin', src) stdin = f'HEAD {clone_rev} refs/heads/b2 {hook_impl.Z40}\n'.encode() ns = hook_impl._run_ns('pre-push', False, args, stdin) assert ns is not None assert ns.all_files is True
def test_does_not_change_mixed_endlines_read(up_to_date, tmpdir, store): fmt = ('repos:\n' '- repo: {}\n' ' rev: {} # definitely the version I want!\r\n' ' hooks:\r\n' ' - id: foo\n' ' # These args are because reasons!\r\n' ' args: [foo, bar, baz]\r\n') cfg = tmpdir.join(C.CONFIG_FILE) expected = fmt.format(up_to_date, git.head_rev(up_to_date)).encode() cfg.write_binary(expected) assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 assert cfg.read_binary() == expected
def make_config_from_repo(repo_path, rev=None, hooks=None, check=True): manifest = load_manifest(os.path.join(repo_path, C.MANIFEST_FILE)) config = { 'repo': 'file://{}'.format(repo_path), 'rev': rev or git.head_rev(repo_path), 'hooks': hooks or [{'id': hook['id']} for hook in manifest], } if check: wrapped = validate({'repos': [config]}, CONFIG_SCHEMA) wrapped = apply_defaults(wrapped, CONFIG_SCHEMA) config, = wrapped['repos'] return config else: return config
def make_config_from_repo(repo_path, rev=None, hooks=None, check=True): manifest = load_manifest(os.path.join(repo_path, C.MANIFEST_FILE)) config = OrderedDict(( ('repo', 'file://{}'.format(repo_path)), ('rev', rev or git.head_rev(repo_path)), ( 'hooks', hooks or [OrderedDict((('id', hook['id']),)) for hook in manifest], ), )) if check: wrapped = validate({'repos': [config]}, CONFIG_SCHEMA) wrapped = apply_defaults(wrapped, CONFIG_SCHEMA) config, = wrapped['repos'] return config else: return config
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)