def validate_manifest(filenames: list[str]) -> int: ret = 0 for filename in filenames: try: clientlib.load_manifest(filename) except clientlib.InvalidManifestError as e: print(e) ret = 1 return ret
def _mark_used_repos(store, all_repos, unused_repos, repo): if repo['repo'] == META: return elif repo['repo'] == LOCAL: for hook in repo['hooks']: deps = hook.get('additional_dependencies') unused_repos.discard(( store.db_repo_name(repo['repo'], deps), C.LOCAL_REPO_VERSION, )) else: key = (repo['repo'], repo['rev']) path = all_repos.get(key) # can't inspect manifest if it isn't cloned if path is None: return try: manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE)) except InvalidManifestError: return else: unused_repos.discard(key) by_id = {hook['id']: hook for hook in manifest} for hook in repo['hooks']: if hook['id'] not in by_id: continue deps = hook.get( 'additional_dependencies', by_id[hook['id']]['additional_dependencies'], ) unused_repos.discard(( store.db_repo_name(repo['repo'], deps), repo['rev'], ))
def try_repo(args): with tmpdir() as tempdir: repo, ref = _repo_ref(tempdir, args.repo, args.ref) store = Store(tempdir) if args.hook: hooks = [{'id': args.hook}] else: repo_path = store.clone(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', 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) return run(config_filename, store, args)
def make_config_from_repo( repo_path, sha=None, hooks=None, check=True, legacy=False, ): filename = C.MANIFEST_FILE_LEGACY if legacy else C.MANIFEST_FILE manifest = load_manifest(os.path.join(repo_path, filename)) config = OrderedDict(( ('repo', repo_path), ('sha', sha or get_head_sha(repo_path)), ( 'hooks', hooks or [OrderedDict((('id', hook['id']), )) for hook in manifest], ), )) if check: wrapped = validate([config], CONFIG_SCHEMA) config, = apply_defaults(wrapped, CONFIG_SCHEMA) return config else: return config
def try_repo(args: argparse.Namespace) -> int: with tmpdir() as tempdir: repo, ref = _repo_ref(tempdir, args.repo, args.ref) store = Store(tempdir) if args.hook: hooks = [{"id": args.hook}] else: repo_path = store.clone(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] config = {"repos": [{"repo": repo, "rev": ref, "hooks": hooks}]} config_s = yaml_dump(config) 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) return run(config_filename, store, args)
def _cloned_repository_hooks( repo_config: Dict[str, Any], store: Store, root_config: Dict[str, Any], ) -> Tuple[Hook, ...]: repo, rev = repo_config['repo'], repo_config['rev'] manifest_path = os.path.join(store.clone(repo, rev), C.MANIFEST_FILE) by_id = {hook['id']: hook for hook in load_manifest(manifest_path)} for hook in repo_config['hooks']: if hook['id'] not in by_id: logger.error( f'`{hook["id"]}` is not present in repository {repo}. ' f'Typo? Perhaps it is introduced in a newer version? ' f'Often `pre-commit autoupdate` fixes this.', ) exit(1) hook_dcts = [ _hook(by_id[hook['id']], hook, root_config=root_config) for hook in repo_config['hooks'] ] return tuple( Hook.create( repo_config['repo'], Prefix(store.clone(repo, rev, hook['additional_dependencies'])), hook, ) for hook in hook_dcts)
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 try_repo(args: argparse.Namespace) -> int: with tmpdir() as tempdir: repo, ref = _repo_ref(tempdir, args.repo, args.ref) store = Store(tempdir) if args.hook: hooks = [{'id': args.hook}] else: repo_path = store.clone(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] config = {'repos': [{'repo': repo, 'rev': ref, 'hooks': hooks}]} config_s = yaml_dump(config) 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) return run(config_filename, store, args)
def _cloned_repository_hooks(repo_config, store, root_config): repo, rev = repo_config['repo'], repo_config['rev'] manifest_path = os.path.join(store.clone(repo, rev), C.MANIFEST_FILE) by_id = {hook['id']: hook for hook in load_manifest(manifest_path)} for hook in repo_config['hooks']: if hook['id'] not in by_id: logger.error( '`{}` is not present in repository {}. ' 'Typo? Perhaps it is introduced in a newer version? ' 'Often `pre-commit autoupdate` fixes this.' .format(hook['id'], repo), ) exit(1) hook_dcts = [ _hook(by_id[hook['id']], hook, root_config=root_config) for hook in repo_config['hooks'] ] return tuple( Hook.create( repo_config['repo'], Prefix(store.clone(repo, rev, hook['additional_dependencies'])), hook, ) for hook in hook_dcts )
def local_python_config(): # 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 ] return {'repo': 'local', 'hooks': hooks}
def manifest_contents(self): default_path = os.path.join(self.repo_path, C.MANIFEST_FILE) legacy_path = os.path.join(self.repo_path, C.MANIFEST_FILE_LEGACY) if os.path.exists(legacy_path) and not os.path.exists(default_path): logger.warning( '{} uses legacy {} to provide hooks.\n' 'In newer versions, this file is called {}\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( self.repo_url, C.MANIFEST_FILE_LEGACY, C.MANIFEST_FILE, ), ) return load_manifest(legacy_path) else: return load_manifest(default_path)
def manifest_contents(self): default_path = os.path.join(self.repo_path, C.MANIFEST_FILE) legacy_path = os.path.join(self.repo_path, C.MANIFEST_FILE_LEGACY) if os.path.exists(legacy_path) and not os.path.exists(default_path): logger.warning( '{} uses legacy {} to provide hooks.\n' 'In newer versions, this file is called {}\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( self.repo_url, C.MANIFEST_FILE_LEGACY, C.MANIFEST_FILE, ) ) return load_manifest(legacy_path) else: return load_manifest(default_path)
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_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 _norm_out(ret[1]) == b"['filename']\nHello World\n"
def _check_hooks_still_exist_at_rev(repo_config, info, store): try: path = store.clone(repo_config['repo'], info.rev) manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE)) except InvalidManifestError as e: raise RepositoryCannotBeUpdatedError(six.text_type(e)) # See if any of our hooks were deleted with the new commits hooks = {hook['id'] for hook in repo_config['hooks']} hooks_missing = hooks - {hook['id'] for hook in manifest} if hooks_missing: raise RepositoryCannotBeUpdatedError( 'Cannot update because the tip of master is missing these hooks:\n' '{}'.format(', '.join(sorted(hooks_missing))), )
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 get_manifest(repo_path: str) -> Tuple[bool, str, List[Dict[str, Any]]]: print(f'*** {repo_path}') with tempfile.TemporaryDirectory() as directory: repo_dir = os.path.join(directory, 'repo') cmd = ('git', 'clone', '--depth', '1', '-q', repo_path, repo_dir) subprocess.check_call(cmd) manifest_path = os.path.join(repo_dir, '.pre-commit-hooks.yaml') # Validate the manifest just to make sure it's ok. manifest = load_manifest(manifest_path) # hooks should not set debugging `verbose: true` flag for hook in manifest: if hook['verbose']: print(f'{repo_path} ({hook["id"]}) sets `verbose: true`') return False, repo_path, [] with open(manifest_path) as f: return True, repo_path, fast_load(f)
def _update_repo(repo_config, store, 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 """ with tmpdir() as repo_path: git.init_repo(repo_path, repo_config['repo']) cmd_output('git', 'fetch', 'origin', 'HEAD', '--tags', cwd=repo_path) tag_cmd = ('git', 'describe', 'FETCH_HEAD', '--tags') if tags_only: tag_cmd += ('--abbrev=0',) else: tag_cmd += ('--exact',) try: rev = cmd_output(*tag_cmd, cwd=repo_path)[1].strip() except CalledProcessError: tag_cmd = ('git', 'rev-parse', 'FETCH_HEAD') rev = cmd_output(*tag_cmd, cwd=repo_path)[1].strip() # Don't bother trying to update if our rev is the same if rev == repo_config['rev']: return repo_config try: path = store.clone(repo_config['repo'], rev) manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE)) except InvalidManifestError as e: raise RepositoryCannotBeUpdatedError(six.text_type(e)) # See if any of our hooks were deleted with the new commits hooks = {hook['id'] for hook in repo_config['hooks']} hooks_missing = hooks - {hook['id'] for hook in manifest} if hooks_missing: raise RepositoryCannotBeUpdatedError( 'Cannot update because the tip of master is missing these hooks:\n' '{}'.format(', '.join(sorted(hooks_missing))), ) # Construct a new config with the head rev new_config = repo_config.copy() new_config['rev'] = rev return new_config
def _update_repo(repo_config, store, 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_path = store.clone(repo_config['repo'], repo_config['rev']) cmd_output('git', 'fetch', cwd=repo_path) tag_cmd = ('git', 'describe', 'origin/master', '--tags') if tags_only: tag_cmd += ('--abbrev=0',) else: tag_cmd += ('--exact',) try: rev = cmd_output(*tag_cmd, cwd=repo_path)[1].strip() except CalledProcessError: tag_cmd = ('git', 'rev-parse', 'origin/master') rev = cmd_output(*tag_cmd, cwd=repo_path)[1].strip() # Don't bother trying to update if our rev is the same if rev == repo_config['rev']: return repo_config try: path = store.clone(repo_config['repo'], rev) manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE)) except InvalidManifestError as e: raise RepositoryCannotBeUpdatedError(six.text_type(e)) # See if any of our hooks were deleted with the new commits hooks = {hook['id'] for hook in repo_config['hooks']} hooks_missing = hooks - {hook['id'] for hook in manifest} if hooks_missing: raise RepositoryCannotBeUpdatedError( 'Cannot update because the tip of master is missing these hooks:\n' '{}'.format(', '.join(sorted(hooks_missing))), ) # Construct a new config with the head rev new_config = repo_config.copy() new_config['rev'] = rev return new_config
def _check_hooks_still_exist_at_rev( repo_config: Dict[str, Any], info: RevInfo, store: Store, ) -> None: try: path = store.clone(repo_config['repo'], info.rev) manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE)) except InvalidManifestError as e: raise RepositoryCannotBeUpdatedError(str(e)) # See if any of our hooks were deleted with the new commits hooks = {hook['id'] for hook in repo_config['hooks']} hooks_missing = hooks - {hook['id'] for hook in manifest} if hooks_missing: raise RepositoryCannotBeUpdatedError( f'Cannot update because the tip of HEAD is missing these hooks:\n' f'{", ".join(sorted(hooks_missing))}', )
def make_config_from_repo(repo_path, sha=None, hooks=None, check=True): manifest = load_manifest(os.path.join(repo_path, C.MANIFEST_FILE)) config = OrderedDict(( ('repo', 'file://{}'.format(repo_path)), ('sha', sha or get_head_sha(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 make_config_from_repo(repo_path, sha=None, hooks=None, check=True): manifest = load_manifest(os.path.join(repo_path, C.MANIFEST_FILE)) config = OrderedDict(( ('repo', 'file://{}'.format(repo_path)), ('sha', sha or git.head_sha(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 _mark_used_repos( store: Store, all_repos: dict[tuple[str, str], str], unused_repos: set[tuple[str, str]], repo: dict[str, Any], ) -> None: if repo['repo'] == META: return elif repo['repo'] == LOCAL: for hook in repo['hooks']: deps = hook.get('additional_dependencies') unused_repos.discard(( store.db_repo_name(repo['repo'], deps), C.LOCAL_REPO_VERSION, )) else: key = (repo['repo'], repo['rev']) path = all_repos.get(key) # can't inspect manifest if it isn't cloned if path is None: return try: manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE)) except InvalidManifestError: return else: unused_repos.discard(key) by_id = {hook['id']: hook for hook in manifest} for hook in repo['hooks']: if hook['id'] not in by_id: continue deps = hook.get( 'additional_dependencies', by_id[hook['id']]['additional_dependencies'], ) unused_repos.discard(( store.db_repo_name(repo['repo'], deps), repo['rev'], ))
def make_config_from_repo( repo_path, sha=None, hooks=None, check=True, legacy=False, ): filename = C.MANIFEST_FILE_LEGACY if legacy else C.MANIFEST_FILE manifest = load_manifest(os.path.join(repo_path, filename)) config = OrderedDict(( ('repo', repo_path), ('sha', sha or get_head_sha(repo_path)), ( 'hooks', hooks or [OrderedDict((('id', hook['id']),)) for hook in manifest], ), )) if check: wrapped = validate([config], CONFIG_SCHEMA) config, = apply_defaults(wrapped, CONFIG_SCHEMA) return config else: return config
import re import pytest from pre_commit.clientlib import load_manifest from pre_commit.constants import MANIFEST_FILE HOOKS = {h['id']: re.compile(h['entry']) for h in load_manifest(MANIFEST_FILE)} @pytest.mark.parametrize( 's', ( 'x = 1 # type: ignore_me', 'x = 1 # type: int', 'x = 1 # type int', 'x = 1 # type: int # noqa', ), ) def test_python_use_type_annotations_positive(s): assert HOOKS['python-use-type-annotations'].search(s) @pytest.mark.parametrize( 's', ( 'x = 1', 'x = 1 # type:ignore', 'x = 1 # type: ignore', 'x = 1 # type: ignore', 'x = 1 # type: ignore # noqa', 'x = 1 # type: ignore # noqa',
def manifest_hooks(self): repo, rev = self.repo_config['repo'], self.repo_config['rev'] repo_path = self.store.clone(repo, rev) manifest_path = os.path.join(repo_path, C.MANIFEST_FILE) return {hook['id']: hook for hook in load_manifest(manifest_path)}
def manifest_contents(self): return load_manifest(os.path.join(self.repo_path, C.MANIFEST_FILE))
def manifest_hooks(self): manifest_path = os.path.join(self._repo_path, C.MANIFEST_FILE) return {hook['id']: hook for hook in load_manifest(manifest_path)}
def pre_commit_manifest(): return load_manifest(MANIFEST_FILE)