def _run_hooks(config, hooks, args, environ): """Actually run the hooks.""" skips = _get_skips(environ) cols = _compute_cols(hooks, args.verbose) filenames = _all_filenames(args) filenames = filter_by_include_exclude(filenames, '', config['exclude']) classifier = Classifier(filenames) retval = 0 for hook in hooks: retval |= _run_single_hook(classifier, hook, args, skips, cols) if retval and config['fail_fast']: break if retval and args.show_diff_on_failure and git.has_diff(): if args.all_files: output.write_line( 'pre-commit hook(s) made changes.\n' 'If you are seeing this message in CI, ' 'reproduce locally with: `pre-commit run --all-files`.\n' 'To run `pre-commit` as part of git workflow, use ' '`pre-commit install`.', ) output.write_line('All changes made by hooks:') # args.color is a boolean. # See user_color function in color.py subprocess.call(( 'git', '--no-pager', 'diff', '--no-ext-diff', '--color={}'.format({True: 'always', False: 'never'}[args.color]), )) return retval
def clean(store): legacy_path = os.path.expanduser('~/.pre-commit') for directory in (store.directory, legacy_path): if os.path.exists(directory): rmtree(directory) output.write_line('Cleaned {}.'.format(directory)) return 0
def install( config_file, store, overwrite=False, hooks=False, hook_type='pre-commit', skip_on_missing_conf=False, ): """Install the pre-commit hooks.""" if cmd_output('git', 'config', 'core.hooksPath', retcode=None)[1].strip(): logger.error( 'Cowardly refusing to install hooks with `core.hooksPath` set.\n' 'hint: `git config --unset-all core.hooksPath`', ) return 1 hook_path, legacy_path = _hook_paths(hook_type) mkdirp(os.path.dirname(hook_path)) # If we have an existing hook, move it to pre-commit.legacy if os.path.lexists(hook_path) and not is_our_script(hook_path): shutil.move(hook_path, legacy_path) # If we specify overwrite, we simply delete the legacy file if overwrite and os.path.exists(legacy_path): os.remove(legacy_path) elif os.path.exists(legacy_path): output.write_line( 'Running in migration mode with existing hooks at {}\n' 'Use -f to use only pre-commit.'.format(legacy_path), ) params = { 'CONFIG': config_file, 'HOOK_TYPE': hook_type, 'INSTALL_PYTHON': sys.executable, 'SKIP_ON_MISSING_CONFIG': skip_on_missing_conf, } with io.open(hook_path, 'w') as hook_file: contents = resource_text('hook-tmpl') before, rest = contents.split(TEMPLATE_START) to_template, after = rest.split(TEMPLATE_END) before = before.replace('#!/usr/bin/env python3', shebang()) hook_file.write(before + TEMPLATE_START) for line in to_template.splitlines(): var = line.split()[0] hook_file.write('{} = {!r}\n'.format(var, params[var])) hook_file.write(TEMPLATE_END + after) make_executable(hook_path) output.write_line('pre-commit installed at {}'.format(hook_path)) # If they requested we install all of the hooks, do so. if hooks: install_hooks(config_file, store) return 0
def main(argv=None): parser = argparse.ArgumentParser() parser.add_argument('--dest', default='pre_commit/resources') args = parser.parse_args(argv) for archive_name, repo, ref in REPOS: output.write_line( 'Making {}.tar.gz for {}@{}'.format(archive_name, repo, ref), ) make_archive(archive_name, repo, ref, args.dest)
def _process_filename_by_line(pattern, filename): retv = 0 with open(filename, 'rb') as f: for line_no, line in enumerate(f, start=1): if pattern.search(line): retv = 1 output.write('{}:{}:'.format(filename, line_no)) output.write_line(line.rstrip(b'\r\n')) return retv
def install( runner, overwrite=False, hooks=False, hook_type='pre-commit', skip_on_missing_conf=False, ): """Install the pre-commit hooks.""" hook_path = runner.get_hook_path(hook_type) legacy_path = hook_path + '.legacy' mkdirp(os.path.dirname(hook_path)) # If we have an existing hook, move it to pre-commit.legacy if os.path.lexists(hook_path) and not is_our_script(hook_path): os.rename(hook_path, legacy_path) # If we specify overwrite, we simply delete the legacy file if overwrite and os.path.exists(legacy_path): os.remove(legacy_path) elif os.path.exists(legacy_path): output.write_line( 'Running in migration mode with existing hooks at {}\n' 'Use -f to use only pre-commit.'.format( legacy_path, ), ) with io.open(hook_path, 'w') as pre_commit_file_obj: if hook_type == 'pre-push': with io.open(resource_filename('pre-push-tmpl')) as f: hook_specific_contents = f.read() elif hook_type == 'commit-msg': with io.open(resource_filename('commit-msg-tmpl')) as f: hook_specific_contents = f.read() elif hook_type == 'pre-commit': hook_specific_contents = '' else: raise AssertionError('Unknown hook type: {}'.format(hook_type)) skip_on_missing_conf = 'true' if skip_on_missing_conf else 'false' contents = io.open(resource_filename('hook-tmpl')).read().format( sys_executable=pipes.quote(sys.executable), hook_type=hook_type, hook_specific=hook_specific_contents, config_file=runner.config_file, skip_on_missing_conf=skip_on_missing_conf, ) pre_commit_file_obj.write(contents) make_executable(hook_path) output.write_line('pre-commit installed at {}'.format(hook_path)) # If they requested we install all of the hooks, do so. if hooks: install_hooks(runner) return 0
def run(runner, args, environ=os.environ): no_stash = args.no_stash or args.all_files or bool(args.files) # Check if we have unresolved merge conflict files and fail fast. if _has_unmerged_paths(runner): 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(runner) and not no_stash: if args.allow_unstaged_config: logger.warn( 'You have an unstaged config file and have specified the ' '--allow-unstaged-config option.\n' 'Note that your config will be stashed before the config is ' 'parsed unless --no-stash is specified.', ) else: logger.error( 'Your .pre-commit-config.yaml is unstaged.\n' '`git add .pre-commit-config.yaml` to fix this.\n' 'Run pre-commit with --allow-unstaged-config to silence 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 no_stash: ctx = noop_context() else: ctx = staged_files_only(runner.cmd_runner) with ctx: repo_hooks = list(get_repo_hooks(runner)) if args.hook: repo_hooks = [ (repo, hook) for repo, hook in repo_hooks if hook['id'] == args.hook ] if not repo_hooks: output.write_line('No hook with id `{}`'.format(args.hook)) return 1 # Filter hooks for stages repo_hooks = [ (repo, hook) for repo, hook in repo_hooks if not hook['stages'] or args.hook_stage in hook['stages'] ] return _run_hooks(repo_hooks, args, environ)
def emit(self, record): output.write_line( '{}{}'.format( color.format_color( '[{}]'.format(record.levelname), LOG_LEVEL_COLORS[record.levelname], self.use_color, ) + ' ', record.getMessage(), ), )
def _log_and_exit(msg, exc, formatted): error_msg = b''.join(( five.to_bytes(msg), b': ', five.to_bytes(type(exc).__name__), b': ', _to_bytes(exc), b'\n', )) output.write(error_msg) store = Store() log_path = os.path.join(store.directory, 'pre-commit.log') output.write_line('Check the log at {}'.format(log_path)) with open(log_path, 'wb') as log: output.write(error_msg, stream=log) output.write_line(formatted, stream=log) raise SystemExit(1)
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 _log_and_exit(msg, exc, formatted): error_msg = b''.join(( five.to_bytes(msg), b': ', five.to_bytes(type(exc).__name__), b': ', _to_bytes(exc), b'\n', )) output.write(error_msg) output.write_line('Check the log at ~/.pre-commit/pre-commit.log') store = Store() store.require_created() with io.open(os.path.join(store.directory, 'pre-commit.log'), 'wb') as log: output.write(error_msg, stream=log) output.write_line(formatted, stream=log) raise PreCommitSystemExit(1)
def _process_filename_at_once(pattern, filename): retv = 0 with open(filename, 'rb') as f: contents = f.read() match = pattern.search(contents) if match: retv = 1 line_no = contents[:match.start()].count(b'\n') output.write('{}:{}:'.format(filename, line_no + 1)) matched_lines = match.group().split(b'\n') matched_lines[0] = contents.split(b'\n')[line_no] output.write_line(b'\n'.join(matched_lines)) return retv
def uninstall(runner, hook_type='pre-commit'): """Uninstall the pre-commit hooks.""" hook_path = runner.get_hook_path(hook_type) legacy_path = hook_path + '.legacy' # If our file doesn't exist or it isn't ours, gtfo. if not os.path.exists(hook_path) or not is_our_script(hook_path): return 0 os.remove(hook_path) output.write_line('{} uninstalled'.format(hook_type)) if os.path.exists(legacy_path): os.rename(legacy_path, hook_path) output.write_line('Restored previous hooks to {}'.format(hook_path)) return 0
def run(runner, 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(runner) and not no_stash: logger.error( 'Your .pre-commit-config.yaml is unstaged.\n' '`git add .pre-commit-config.yaml` 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 no_stash: ctx = noop_context() else: ctx = staged_files_only(runner.store.directory) with ctx: repo_hooks = list(get_repo_hooks(runner)) if args.hook: repo_hooks = [ (repo, hook) for repo, hook in repo_hooks if hook['id'] == args.hook ] if not repo_hooks: output.write_line('No hook with id `{}`'.format(args.hook)) return 1 # Filter hooks for stages repo_hooks = [ (repo, hook) for repo, hook in repo_hooks if not hook['stages'] or args.hook_stage in hook['stages'] ] return _run_hooks(runner.config, repo_hooks, args, environ)
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 run(argv=None): parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*', help=filenames_help) parser.add_argument( '-V', '--version', action='version', version='%(prog)s {}'.format( pkg_resources.get_distribution('pre-commit').version ) ) args = parser.parse_args(argv) retval = 0 for filename in args.filenames: try: validate_strategy(filename) except exception_cls as e: output.write_line(e.args[0]) retval = 1 return retval
def autoupdate(config_file, store, tags_only, repos=()): """Auto-update the pre-commit config to the latest versions of repos.""" migrate_config(config_file, quiet=True) retv = 0 output_repos = [] changed = False input_config = load_config(config_file) for repo_config in input_config['repos']: if ( repo_config['repo'] in {LOCAL, META} or # Skip updating any repo_configs that aren't for the specified repo repos and repo_config['repo'] not in repos ): output_repos.append(repo_config) continue output.write('Updating {}...'.format(repo_config['repo'])) try: new_repo_config = _update_repo(repo_config, store, tags_only) except RepositoryCannotBeUpdatedError as error: output.write_line(error.args[0]) output_repos.append(repo_config) retv = 1 continue if new_repo_config['rev'] != repo_config['rev']: changed = True output.write_line( 'updating {} -> {}.'.format( repo_config['rev'], new_repo_config['rev'], ), ) output_repos.append(new_repo_config) else: output.write_line('already up to date.') output_repos.append(repo_config) if changed: output_config = input_config.copy() output_config['repos'] = output_repos _write_new_config_file(config_file, output_config) return retv
def autoupdate(runner): """Auto-update the pre-commit config to the latest versions of repos.""" retv = 0 output_configs = [] changed = False input_configs = load_config( runner.config_file_path, load_strategy=ordered_load, ) for repo_config in input_configs: if is_local_hooks(repo_config): output_configs.append(repo_config) continue output.write('Updating {}...'.format(repo_config['repo'])) try: new_repo_config = _update_repository(repo_config, runner) except RepositoryCannotBeUpdatedError as error: output.write_line(error.args[0]) output_configs.append(repo_config) retv = 1 continue if new_repo_config['sha'] != repo_config['sha']: changed = True output.write_line('updating {} -> {}.'.format( repo_config['sha'], new_repo_config['sha'], )) output_configs.append(new_repo_config) else: output.write_line('already up to date.') output_configs.append(repo_config) if changed: with open(runner.config_file_path, 'w') as config_file: config_file.write( ordered_dump( remove_defaults(output_configs, CONFIG_JSON_SCHEMA), **C.YAML_DUMP_KWARGS ) ) return retv
def autoupdate(runner, tags_only): """Auto-update the pre-commit config to the latest versions of repos.""" migrate_config(runner, quiet=True) retv = 0 output_repos = [] changed = False input_config = load_config(runner.config_file_path) for repo_config in input_config['repos']: if is_local_repo(repo_config): output_repos.append(repo_config) continue output.write('Updating {}...'.format(repo_config['repo'])) try: new_repo_config = _update_repo(repo_config, runner, tags_only) except RepositoryCannotBeUpdatedError as error: output.write_line(error.args[0]) output_repos.append(repo_config) retv = 1 continue if new_repo_config['sha'] != repo_config['sha']: changed = True output.write_line('updating {} -> {}.'.format( repo_config['sha'], new_repo_config['sha'], )) output_repos.append(new_repo_config) else: output.write_line('already up to date.') output_repos.append(repo_config) if changed: output_config = input_config.copy() output_config['repos'] = output_repos _write_new_config_file(runner.config_file_path, output_config) return retv
def _run_single_hook(filenames, hook, repo, args, skips, cols): include, exclude = hook['files'], hook['exclude'] filenames = _filter_by_include_exclude(filenames, include, exclude) types, exclude_types = hook['types'], hook['exclude_types'] filenames = _filter_by_types(filenames, types, exclude_types) if hook['id'] in skips: output.write( get_hook_message( _hook_msg_start(hook, args.verbose), end_msg=SKIPPED, end_color=color.YELLOW, use_color=args.color, cols=cols, )) return 0 elif not filenames and not hook['always_run']: output.write( get_hook_message( _hook_msg_start(hook, args.verbose), postfix=NO_FILES, end_msg=SKIPPED, end_color=color.TURQUOISE, use_color=args.color, cols=cols, )) return 0 # Print the hook and the dots first in case the hook takes hella long to # run. output.write( get_hook_message( _hook_msg_start(hook, args.verbose), end_len=6, cols=cols, )) sys.stdout.flush() diff_before = cmd_output( 'git', 'diff', '--no-ext-diff', retcode=None, encoding=None, ) retcode, stdout, stderr = repo.run_hook( hook, tuple(filenames) if hook['pass_filenames'] else (), ) diff_after = cmd_output( 'git', 'diff', '--no-ext-diff', retcode=None, encoding=None, ) file_modifications = diff_before != diff_after # If the hook makes changes, fail the commit if file_modifications: retcode = 1 if retcode: retcode = 1 print_color = color.RED pass_fail = 'Failed' else: retcode = 0 print_color = color.GREEN pass_fail = 'Passed' output.write_line(color.format_color(pass_fail, print_color, args.color)) if (stdout or stderr or file_modifications) and (retcode or args.verbose): output.write_line('hookid: {}\n'.format(hook['id'])) # Print a message if failing due to file modifications if file_modifications: output.write('Files were modified by this hook.') if stdout or stderr: output.write_line(' Additional output:') output.write_line() for out in (stdout, stderr): assert type(out) is bytes, type(out) if out.strip(): output.write_line(out.strip(), logfile_name=hook['log_file']) output.write_line() return retcode
def main(): for archive_name, repo, ref in REPOS: output.write_line('Making {}.tar.gz for {}@{}'.format( archive_name, repo, ref, )) make_archive(archive_name, repo, ref, RESOURCES_DIR)
def gc(store): with store.exclusive_lock(): repos_removed = _gc_repos(store) output.write_line('{} repo(s) removed.'.format(repos_removed)) return 0
from pre_commit.output import write_line write_line("msg1") print("msg2")
def _log_line(*s): # type: (*str) -> None output.write_line(*s, stream=log)
# skip html stuff elif re.match(r"({% (extends|block)|<!--).*", line): continue else: h_lines.append(line) if len(h_lines) == 3: break return h_lines if __name__ == "__main__": retv = 0 for filename in sys.argv[1:]: filepath = Path(filename) if not filepath.is_file(): continue header_lines = get_header_lines(filepath) if len(header_lines) < 3: retv |= 1 output.write_line(f"{filename}: (not enough lines)") else: header = "".join(header_lines) if header != EXPECTED_HEADER: retv |= 1 output.write(f"{filename}:\n {' '.join(header_lines)}") else: retv |= 0 sys.exit(retv)
def _run_single_hook(classifier, hook, args, skips, cols): filenames = classifier.filenames_for_hook(hook) if hook.language == 'pcre': logger.warning( '`{}` (from {}) uses the deprecated pcre language.\n' 'The pcre language is scheduled for removal in pre-commit 2.x.\n' 'The pygrep language is a more portable (and usually drop-in) ' 'replacement.'.format(hook.id, hook.src), ) if hook.id in skips or hook.alias in skips: output.write( get_hook_message( _hook_msg_start(hook, args.verbose), end_msg=SKIPPED, end_color=color.YELLOW, use_color=args.color, cols=cols, ), ) return 0 elif not filenames and not hook.always_run: output.write( get_hook_message( _hook_msg_start(hook, args.verbose), postfix=NO_FILES, end_msg=SKIPPED, end_color=color.TURQUOISE, use_color=args.color, cols=cols, ), ) return 0 # Print the hook and the dots first in case the hook takes hella long to # run. output.write( get_hook_message( _hook_msg_start(hook, args.verbose), end_len=6, cols=cols, ), ) sys.stdout.flush() diff_before = cmd_output( 'git', 'diff', '--no-ext-diff', retcode=None, encoding=None, ) retcode, stdout, stderr = hook.run( tuple(filenames) if hook.pass_filenames else (), ) diff_after = cmd_output( 'git', 'diff', '--no-ext-diff', retcode=None, encoding=None, ) file_modifications = diff_before != diff_after # If the hook makes changes, fail the commit if file_modifications: retcode = 1 if retcode: retcode = 1 print_color = color.RED pass_fail = 'Failed' else: retcode = 0 print_color = color.GREEN pass_fail = 'Passed' output.write_line(color.format_color(pass_fail, print_color, args.color)) if ( (stdout or stderr or file_modifications) and (retcode or args.verbose or hook.verbose) ): output.write_line('hookid: {}\n'.format(hook.id)) # Print a message if failing due to file modifications if file_modifications: output.write('Files were modified by this hook.') if stdout or stderr: output.write_line(' Additional output:') output.write_line() for out in (stdout, stderr): assert type(out) is bytes, type(out) if out.strip(): output.write_line(out.strip(), logfile_name=hook.log_file) output.write_line() return retcode
def _run_single_hook(classifier, hook, args, skips, cols): filenames = classifier.filenames_for_hook(hook) if hook.language == 'pcre': logger.warning( '`{}` (from {}) uses the deprecated pcre language.\n' 'The pcre language is scheduled for removal in pre-commit 2.x.\n' 'The pygrep language is a more portable (and usually drop-in) ' 'replacement.'.format(hook.id, hook.src), ) if hook.id in skips or hook.alias in skips: output.write(get_hook_message( _hook_msg_start(hook, args.verbose), end_msg=SKIPPED, end_color=color.YELLOW, use_color=args.color, cols=cols, )) return 0 elif not filenames and not hook.always_run: output.write(get_hook_message( _hook_msg_start(hook, args.verbose), postfix=NO_FILES, end_msg=SKIPPED, end_color=color.TURQUOISE, use_color=args.color, cols=cols, )) return 0 # Print the hook and the dots first in case the hook takes hella long to # run. output.write(get_hook_message( _hook_msg_start(hook, args.verbose), end_len=6, cols=cols, )) sys.stdout.flush() diff_before = cmd_output( 'git', 'diff', '--no-ext-diff', retcode=None, encoding=None, ) retcode, stdout, stderr = hook.run( tuple(filenames) if hook.pass_filenames else (), ) diff_after = cmd_output( 'git', 'diff', '--no-ext-diff', retcode=None, encoding=None, ) file_modifications = diff_before != diff_after # If the hook makes changes, fail the commit if file_modifications: retcode = 1 if retcode: retcode = 1 print_color = color.RED pass_fail = 'Failed' else: retcode = 0 print_color = color.GREEN pass_fail = 'Passed' output.write_line(color.format_color(pass_fail, print_color, args.color)) if ( (stdout or stderr or file_modifications) and (retcode or args.verbose or hook.verbose) ): output.write_line('hookid: {}\n'.format(hook.id)) # Print a message if failing due to file modifications if file_modifications: output.write('Files were modified by this hook.') if stdout or stderr: output.write_line(' Additional output:') output.write_line() for out in (stdout, stderr): assert type(out) is bytes, type(out) if out.strip(): output.write_line(out.strip(), logfile_name=hook.log_file) output.write_line() return retcode
def _run_single_hook(hook, repo, args, skips, cols): filenames = get_filenames(args, hook.get('files', '^$'), hook['exclude']) if hook['id'] in skips: output.write(get_hook_message( _hook_msg_start(hook, args.verbose), end_msg=SKIPPED, end_color=color.YELLOW, use_color=args.color, cols=cols, )) return 0 elif not filenames and not hook['always_run']: output.write(get_hook_message( _hook_msg_start(hook, args.verbose), postfix=NO_FILES, end_msg=SKIPPED, end_color=color.TURQUOISE, use_color=args.color, cols=cols, )) return 0 # Print the hook and the dots first in case the hook takes hella long to # run. output.write(get_hook_message( _hook_msg_start(hook, args.verbose), end_len=6, cols=cols, )) sys.stdout.flush() diff_before = cmd_output( 'git', 'diff', '--no-ext-diff', retcode=None, encoding=None, ) retcode, stdout, stderr = repo.run_hook( hook, tuple(filenames) if hook['pass_filenames'] else (), ) diff_after = cmd_output( 'git', 'diff', '--no-ext-diff', retcode=None, encoding=None, ) file_modifications = diff_before != diff_after # If the hook makes changes, fail the commit if file_modifications: retcode = 1 if retcode: retcode = 1 print_color = color.RED pass_fail = 'Failed' else: retcode = 0 print_color = color.GREEN pass_fail = 'Passed' output.write_line(color.format_color(pass_fail, print_color, args.color)) if (stdout or stderr or file_modifications) and (retcode or args.verbose): output.write_line('hookid: {}\n'.format(hook['id'])) # Print a message if failing due to file modifications if file_modifications: output.write('Files were modified by this hook.') if stdout or stderr: output.write_line(' Additional output:') output.write_line() for out in (stdout, stderr): assert type(out) is bytes, type(out) if out.strip(): output.write_line(out.strip(), logfile_name=hook['log_file']) output.write_line() return retcode
def main(argv=None): argv = argv if argv is not None else sys.argv[1:] for arg in argv: output.write_line(arg)
def main(argv: Optional[Sequence[str]] = None) -> int: argv = argv if argv is not None else sys.argv[1:] for arg in argv: output.write_line(arg) return 0
def _run_single_hook( classifier: Classifier, hook: Hook, skips: Set[str], cols: int, diff_before: bytes, verbose: bool, use_color: bool, ) -> Tuple[bool, bytes]: filenames = classifier.filenames_for_hook(hook) if hook.id in skips or hook.alias in skips: output.write( _full_msg( start=hook.name, end_msg=SKIPPED, end_color=color.YELLOW, use_color=use_color, cols=cols, ), ) duration = None retcode = 0 diff_after = diff_before files_modified = False out = b'' elif not filenames and not hook.always_run: output.write( _full_msg( start=hook.name, postfix=NO_FILES, end_msg=SKIPPED, end_color=color.TURQUOISE, use_color=use_color, cols=cols, ), ) duration = None retcode = 0 diff_after = diff_before files_modified = False out = b'' else: # print hook and dots first in case the hook takes a while to run output.write(_start_msg(start=hook.name, end_len=6, cols=cols)) if not hook.pass_filenames: filenames = () time_before = time.time() language = languages[hook.language] retcode, out = language.run_hook(hook, filenames, use_color) duration = round(time.time() - time_before, 2) or 0 diff_after = _get_diff() # if the hook makes changes, fail the commit files_modified = diff_before != diff_after if retcode or files_modified: print_color = color.RED status = 'Failed' else: print_color = color.GREEN status = 'Passed' output.write_line(color.format_color(status, print_color, use_color)) if verbose or hook.verbose or retcode or files_modified: _subtle_line(f'- hook id: {hook.id}', use_color) if (verbose or hook.verbose) and duration is not None: _subtle_line(f'- duration: {duration}s', use_color) if retcode: _subtle_line(f'- exit code: {retcode}', use_color) # Print a message if failing due to file modifications if files_modified: _subtle_line('- files were modified by this hook', use_color) if out.strip(): output.write_line() output.write_line_b(out.strip(), logfile_name=hook.log_file) output.write_line() return files_modified or bool(retcode), diff_after
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 and args.remote_branch: environ['PRE_COMMIT_REMOTE_BRANCH'] = args.remote_branch 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 if args.is_squash_merge: environ['PRE_COMMIT_IS_SQUASH_MERGE'] = args.is_squash_merge # 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 _subtle_line(s: str, use_color: bool) -> None: output.write_line(color.format_color(s, color.SUBTLE, use_color))
def gc(store: Store) -> int: with store.exclusive_lock(): repos_removed = _gc_repos(store) output.write_line(f'{repos_removed} repo(s) removed.') return 0
def clean(runner): if os.path.exists(runner.store.directory): rmtree(runner.store.directory) output.write_line('Cleaned {}.'.format(runner.store.directory)) return 0
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.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")