def test_gc_unused_local_repo_with_env(store, in_git_dir, cap_out): config = { 'repo': 'local', 'hooks': [{ 'id': 'flake8', 'name': 'flake8', 'entry': 'flake8', # a `language: python` local hook will create an environment 'types': ['python'], 'language': 'python', }], } write_config('.', config) store.mark_config_used(C.CONFIG_FILE) # this causes the repositories to be created all_hooks(load_config(C.CONFIG_FILE), store) 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_default_language_version(store, local_python_config): config: Dict[str, Any] = { 'default_language_version': {'python': 'fake'}, 'default_stages': ['commit'], 'repos': [local_python_config], } # `language_version` was not set, should default hook, = all_hooks(config, store) assert hook.language_version == 'fake' # `language_version` is set, should not default config['repos'][0]['hooks'][0]['language_version'] = 'fake2' hook, = all_hooks(config, store) assert hook.language_version == 'fake2'
def test_default_stages(store, local_python_config): config: Dict[str, Any] = { 'default_language_version': {'python': C.DEFAULT}, 'default_stages': ['commit'], 'repos': [local_python_config], } # `stages` was not set, should default hook, = all_hooks(config, store) assert hook.stages == ['commit'] # `stages` is set, should not default config['repos'][0]['hooks'][0]['stages'] = ['push'] hook, = all_hooks(config, store) assert hook.stages == ['push']
def _get_hook_no_install(repo_config, store, hook_id): config = {'repos': [repo_config]} config = cfgv.validate(config, CONFIG_SCHEMA) config = cfgv.apply_defaults(config, CONFIG_SCHEMA) hooks = all_hooks(config, store) hook, = [hook for hook in hooks if hook.id == hook_id] return hook
def check_all_hooks_match_files(config_file: str) -> int: classifier = Classifier(git.get_all_files()) retv = 0 for hook in all_hooks(load_config(config_file), Store()): if hook.always_run or hook.language == 'fail': continue elif not classifier.filenames_for_hook(hook): print(f'{hook.id} does not apply to this repository') retv = 1 return retv
def check_all_hooks_match_files(config_file): classifier = Classifier(git.get_all_files()) retv = 0 for hook in all_hooks(load_config(config_file), Store()): if hook.always_run or hook.language == 'fail': continue elif not classifier.filenames_for_hook(hook): print('{} does not apply to this repository'.format(hook.id)) retv = 1 return retv
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 run( config_file: str, store: Store, args: argparse.Namespace, environ: EnvironT = os.environ, ) -> int: stash = not args.all_files and not args.files # Check if we have unresolved merge conflict files and fail fast. if _has_unmerged_paths(): logger.error('Unmerged files. Resolve before committing.') return 1 if bool(args.source) != bool(args.origin): logger.error('Specify both --origin and --source.') return 1 if stash and _has_unstaged_config(config_file): logger.error( f'Your pre-commit configuration is unstaged.\n' f'`git add {config_file}` to fix this.', ) return 1 # Expose origin / source as environment variables for hooks to consume if args.origin and args.source: environ['PRE_COMMIT_ORIGIN'] = args.origin environ['PRE_COMMIT_SOURCE'] = args.source if args.remote_name and args.remote_url: environ['PRE_COMMIT_REMOTE_NAME'] = args.remote_name environ['PRE_COMMIT_REMOTE_URL'] = args.remote_url with contextlib.ExitStack() as exit_stack: if stash: exit_stack.enter_context(staged_files_only(store.directory)) config = load_config(config_file) hooks = [ hook for hook in all_hooks(config, store) if not args.hook or hook.id == args.hook or hook.alias == args.hook if args.hook_stage in hook.stages ] if args.hook and not hooks: output.write_line( f'No hook with id `{args.hook}` in stage `{args.hook_stage}`', ) return 1 install_hook_envs(hooks, store) return _run_hooks(config, hooks, args, environ) # https://github.com/python/mypy/issues/7726 raise AssertionError('unreachable')
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_gc_unused_local_repo_with_env(store, in_git_dir, cap_out): config = { 'repo': 'local', 'hooks': [{ 'id': 'flake8', 'name': 'flake8', 'entry': 'flake8', # a `language: python` local hook will create an environment 'types': ['python'], 'language': 'python', }], } write_config('.', config) store.mark_config_used(C.CONFIG_FILE) # this causes the repositories to be created all_hooks(load_config(C.CONFIG_FILE), store) 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 find_hook(args: argparse.Namespace, store: Store) -> Hook: config = load_config(args.config) hooks = [ hook for hook in all_hooks(config, store) if not args.hook or hook.id == args.hook or hook.alias == args.hook if args.hook_stage in hook.stages ] if not hooks: raise ValueError( f"No hook with id `{args.hook}` in stage `{args.hook_stage}`") install_hook_envs(hooks, store) return hooks[0]
def check_all_hooks_match_files(config_file): files = git.get_all_files() retv = 0 for hook in all_hooks(load_config(config_file), Store()): if hook.always_run or hook.language == 'fail': continue include, exclude = hook.files, hook.exclude filtered = _filter_by_include_exclude(files, include, exclude) types, exclude_types = hook.types, hook.exclude_types filtered = _filter_by_types(filtered, types, exclude_types) if not filtered: print('{} does not apply to this repository'.format(hook.id)) retv = 1 return retv
def run(config_file, store, args, environ=os.environ): no_stash = args.all_files or bool(args.files) # Check if we have unresolved merge conflict files and fail fast. if _has_unmerged_paths(): logger.error('Unmerged files. Resolve before committing.') return 1 if bool(args.source) != bool(args.origin): logger.error('Specify both --origin and --source.') return 1 if _has_unstaged_config(config_file) and not no_stash: logger.error( 'Your pre-commit configuration is unstaged.\n' '`git add {}` to fix this.'.format(config_file), ) return 1 # Expose origin / source as environment variables for hooks to consume if args.origin and args.source: environ['PRE_COMMIT_ORIGIN'] = args.origin environ['PRE_COMMIT_SOURCE'] = args.source if no_stash: ctx = noop_context() else: ctx = staged_files_only(store.directory) with ctx: config = load_config(config_file) hooks = [ hook for hook in all_hooks(config, store) if not args.hook or hook.id == args.hook or hook.alias == args.hook if args.hook_stage in hook.stages ] if args.hook and not hooks: output.write_line( 'No hook with id `{}` in stage `{}`'.format( args.hook, args.hook_stage, ), ) return 1 install_hook_envs(hooks, store) return _run_hooks(config, hooks, args, environ)
def check_all_hooks_match_files(config_file: str) -> int: config = load_config(config_file) classifier = Classifier.from_config( git.get_all_files(), config["files"], config["exclude"], ) retv = 0 for hook in all_hooks(config, Store()): if hook.always_run or hook.language == "fail": continue elif not classifier.filenames_for_hook(hook): print(f"{hook.id} does not apply to this repository") retv = 1 return retv
def run(config_file, store, args, environ=os.environ): no_stash = args.all_files or bool(args.files) # Check if we have unresolved merge conflict files and fail fast. if _has_unmerged_paths(): logger.error('Unmerged files. Resolve before committing.') return 1 if bool(args.source) != bool(args.origin): logger.error('Specify both --origin and --source.') return 1 if _has_unstaged_config(config_file) and not no_stash: logger.error( 'Your pre-commit configuration is unstaged.\n' '`git add {}` to fix this.'.format(config_file), ) return 1 # Expose origin / source as environment variables for hooks to consume if args.origin and args.source: environ['PRE_COMMIT_ORIGIN'] = args.origin environ['PRE_COMMIT_SOURCE'] = args.source if no_stash: ctx = noop_context() else: ctx = staged_files_only(store.directory) with ctx: config = load_config(config_file) hooks = [ hook for hook in all_hooks(config, store) if not args.hook or hook.id == args.hook or hook.alias == args.hook if args.hook_stage in hook.stages ] if args.hook and not hooks: output.write_line('No hook with id `{}`'.format(args.hook)) return 1 install_hook_envs(hooks, store) return _run_hooks(config, hooks, args, environ)
def install_hooks(config_file: str, store: Store) -> int: install_hook_envs(all_hooks(load_config(config_file), store), store) return 0
def run( config_file: str, store: Store, args: argparse.Namespace, environ: MutableMapping[str, str] = os.environ, ) -> int: stash = not args.all_files and not args.files # Check if we have unresolved merge conflict files and fail fast. if _has_unmerged_paths(): logger.error('Unmerged files. Resolve before committing.') return 1 if bool(args.from_ref) != bool(args.to_ref): logger.error('Specify both --from-ref and --to-ref.') return 1 if stash and _has_unstaged_config(config_file): logger.error( f'Your pre-commit configuration is unstaged.\n' f'`git add {config_file}` to fix this.', ) return 1 if (args.hook_stage in {'prepare-commit-msg', 'commit-msg'} and not args.commit_msg_filename): logger.error( f'`--commit-msg-filename` is required for ' f'`--hook-stage {args.hook_stage}`', ) return 1 # prevent recursive post-checkout hooks (#1418) if (args.hook_stage == 'post-checkout' and environ.get('_PRE_COMMIT_SKIP_POST_CHECKOUT')): return 0 # Expose from-ref / to-ref as environment variables for hooks to consume if args.from_ref and args.to_ref: # legacy names environ['PRE_COMMIT_ORIGIN'] = args.from_ref environ['PRE_COMMIT_SOURCE'] = args.to_ref # new names environ['PRE_COMMIT_FROM_REF'] = args.from_ref environ['PRE_COMMIT_TO_REF'] = args.to_ref if args.remote_name and args.remote_url: environ['PRE_COMMIT_REMOTE_NAME'] = args.remote_name environ['PRE_COMMIT_REMOTE_URL'] = args.remote_url if args.checkout_type: environ['PRE_COMMIT_CHECKOUT_TYPE'] = args.checkout_type # Set pre_commit flag environ['PRE_COMMIT'] = '1' with contextlib.ExitStack() as exit_stack: if stash: exit_stack.enter_context(staged_files_only(store.directory)) config = load_config(config_file) hooks = [ hook for hook in all_hooks(config, store) if not args.hook or hook.id == args.hook or hook.alias == args.hook if args.hook_stage in hook.stages ] if args.hook and not hooks: output.write_line( f'No hook with id `{args.hook}` in stage `{args.hook_stage}`', ) return 1 install_hook_envs(hooks, store) return _run_hooks(config, hooks, args, environ) # https://github.com/python/mypy/issues/7726 raise AssertionError('unreachable')
def install_hooks(config_file, store): install_hook_envs(all_hooks(load_config(config_file), store), store)
def install_hooks(config_file, store): install_hook_envs(all_hooks(load_config(config_file), store), store)