From 63fad53303381388673073de580a32088a4ef0fe Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 24 Mar 2020 22:59:15 +0100 Subject: Adding upstream version 2.2.0. Signed-off-by: Daniel Baumann --- pre_commit/commands/hook_impl.py | 187 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 pre_commit/commands/hook_impl.py (limited to 'pre_commit/commands/hook_impl.py') diff --git a/pre_commit/commands/hook_impl.py b/pre_commit/commands/hook_impl.py new file mode 100644 index 0000000..5ff4555 --- /dev/null +++ b/pre_commit/commands/hook_impl.py @@ -0,0 +1,187 @@ +import argparse +import os.path +import subprocess +import sys +from typing import Optional +from typing import Sequence +from typing import Tuple + +from pre_commit.commands.run import run +from pre_commit.envcontext import envcontext +from pre_commit.parse_shebang import normalize_cmd +from pre_commit.store import Store + +Z40 = '0' * 40 + + +def _run_legacy( + hook_type: str, + hook_dir: str, + args: Sequence[str], +) -> Tuple[int, bytes]: + if os.environ.get('PRE_COMMIT_RUNNING_LEGACY'): + raise SystemExit( + f"bug: pre-commit's script is installed in migration mode\n" + f'run `pre-commit install -f --hook-type {hook_type}` to fix ' + f'this\n\n' + f'Please report this bug at ' + f'https://github.com/pre-commit/pre-commit/issues', + ) + + if hook_type == 'pre-push': + stdin = sys.stdin.buffer.read() + else: + stdin = b'' + + # not running in legacy mode + legacy_hook = os.path.join(hook_dir, f'{hook_type}.legacy') + if not os.access(legacy_hook, os.X_OK): + return 0, stdin + + with envcontext((('PRE_COMMIT_RUNNING_LEGACY', '1'),)): + cmd = normalize_cmd((legacy_hook, *args)) + return subprocess.run(cmd, input=stdin).returncode, stdin + + +def _validate_config( + retv: int, + config: str, + skip_on_missing_config: bool, +) -> None: + if not os.path.isfile(config): + if skip_on_missing_config or os.getenv('PRE_COMMIT_ALLOW_NO_CONFIG'): + print(f'`{config}` config file not found. Skipping `pre-commit`.') + raise SystemExit(retv) + else: + print( + f'No {config} file was found\n' + f'- To temporarily silence this, run ' + f'`PRE_COMMIT_ALLOW_NO_CONFIG=1 git ...`\n' + f'- To permanently silence this, install pre-commit with the ' + f'--allow-missing-config option\n' + f'- To uninstall pre-commit run `pre-commit uninstall`', + ) + raise SystemExit(1) + + +def _ns( + hook_type: str, + color: bool, + *, + all_files: bool = False, + from_ref: Optional[str] = None, + to_ref: Optional[str] = None, + remote_name: Optional[str] = None, + remote_url: Optional[str] = None, + commit_msg_filename: Optional[str] = None, + checkout_type: Optional[str] = None, +) -> argparse.Namespace: + return argparse.Namespace( + color=color, + hook_stage=hook_type.replace('pre-', ''), + from_ref=from_ref, + to_ref=to_ref, + remote_name=remote_name, + remote_url=remote_url, + commit_msg_filename=commit_msg_filename, + all_files=all_files, + checkout_type=checkout_type, + files=(), + hook=None, + verbose=False, + show_diff_on_failure=False, + ) + + +def _rev_exists(rev: str) -> bool: + return not subprocess.call(('git', 'rev-list', '--quiet', rev)) + + +def _pre_push_ns( + color: bool, + args: Sequence[str], + stdin: bytes, +) -> Optional[argparse.Namespace]: + remote_name = args[0] + remote_url = args[1] + + for line in stdin.decode().splitlines(): + _, local_sha, _, remote_sha = line.split() + if local_sha == Z40: + continue + elif remote_sha != Z40 and _rev_exists(remote_sha): + return _ns( + 'pre-push', color, + from_ref=remote_sha, to_ref=local_sha, + remote_name=remote_name, remote_url=remote_url, + ) + else: + # ancestors not found in remote + ancestors = subprocess.check_output(( + 'git', 'rev-list', local_sha, '--topo-order', '--reverse', + '--not', f'--remotes={remote_name}', + )).decode().strip() + if not ancestors: + continue + else: + first_ancestor = ancestors.splitlines()[0] + cmd = ('git', 'rev-list', '--max-parents=0', local_sha) + roots = set(subprocess.check_output(cmd).decode().splitlines()) + if first_ancestor in roots: + # pushing the whole tree including root commit + return _ns( + 'pre-push', color, + all_files=True, + remote_name=remote_name, remote_url=remote_url, + ) + else: + rev_cmd = ('git', 'rev-parse', f'{first_ancestor}^') + source = subprocess.check_output(rev_cmd).decode().strip() + return _ns( + 'pre-push', color, + from_ref=source, to_ref=local_sha, + remote_name=remote_name, remote_url=remote_url, + ) + + # nothing to push + return None + + +def _run_ns( + hook_type: str, + color: bool, + args: Sequence[str], + stdin: bytes, +) -> Optional[argparse.Namespace]: + if hook_type == 'pre-push': + return _pre_push_ns(color, args, stdin) + elif hook_type in {'prepare-commit-msg', 'commit-msg'}: + return _ns(hook_type, color, commit_msg_filename=args[0]) + elif hook_type in {'pre-merge-commit', 'pre-commit'}: + return _ns(hook_type, color) + elif hook_type == 'post-checkout': + return _ns( + hook_type, color, + from_ref=args[0], to_ref=args[1], checkout_type=args[2], + ) + else: + raise AssertionError(f'unexpected hook type: {hook_type}') + + +def hook_impl( + store: Store, + *, + config: str, + color: bool, + hook_type: str, + hook_dir: str, + skip_on_missing_config: bool, + args: Sequence[str], +) -> int: + retv, stdin = _run_legacy(hook_type, hook_dir, args) + _validate_config(retv, config, skip_on_missing_config) + ns = _run_ns(hook_type, color, args, stdin) + if ns is None: + return retv + else: + return retv | run(config, store, ns) -- cgit v1.2.3