mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-01-14 04:50:20 -06:00
186 lines
5.4 KiB
Python
Executable File
186 lines
5.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""File generated by pre-commit: https://pre-commit.com"""
|
|
from __future__ import print_function
|
|
|
|
import distutils.spawn
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
# work around https://github.com/Homebrew/homebrew-core/issues/30445
|
|
os.environ.pop('__PYVENV_LAUNCHER__', None)
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
Z40 = '0' * 40
|
|
ID_HASH = '138fd403232d2ddd5efb44317e38bf03'
|
|
# start templated
|
|
CONFIG = None
|
|
HOOK_TYPE = None
|
|
INSTALL_PYTHON = None
|
|
SKIP_ON_MISSING_CONFIG = None
|
|
# end templated
|
|
|
|
|
|
class EarlyExit(RuntimeError):
|
|
pass
|
|
|
|
|
|
class FatalError(RuntimeError):
|
|
pass
|
|
|
|
|
|
def _norm_exe(exe):
|
|
"""Necessary for shebang support on windows.
|
|
|
|
roughly lifted from `identify.identify.parse_shebang`
|
|
"""
|
|
with open(exe, 'rb') as f:
|
|
if f.read(2) != b'#!':
|
|
return ()
|
|
try:
|
|
first_line = f.readline().decode('UTF-8')
|
|
except UnicodeDecodeError:
|
|
return ()
|
|
|
|
cmd = first_line.split()
|
|
if cmd[0] == '/usr/bin/env':
|
|
del cmd[0]
|
|
return tuple(cmd)
|
|
|
|
|
|
def _run_legacy():
|
|
if __file__.endswith('.legacy'):
|
|
raise SystemExit(
|
|
"bug: pre-commit's script is installed in migration mode\n"
|
|
'run `pre-commit install -f --hook-type {}` to fix this\n\n'
|
|
'Please report this bug at '
|
|
'https://github.com/pre-commit/pre-commit/issues'.format(
|
|
HOOK_TYPE,
|
|
),
|
|
)
|
|
|
|
if HOOK_TYPE == 'pre-push':
|
|
stdin = getattr(sys.stdin, 'buffer', sys.stdin).read()
|
|
else:
|
|
stdin = None
|
|
|
|
legacy_hook = os.path.join(HERE, '{}.legacy'.format(HOOK_TYPE))
|
|
if os.access(legacy_hook, os.X_OK):
|
|
cmd = _norm_exe(legacy_hook) + (legacy_hook,) + tuple(sys.argv[1:])
|
|
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE if stdin else None)
|
|
proc.communicate(stdin)
|
|
return proc.returncode, stdin
|
|
else:
|
|
return 0, stdin
|
|
|
|
|
|
def _validate_config():
|
|
cmd = ('git', 'rev-parse', '--show-toplevel')
|
|
top_level = subprocess.check_output(cmd).decode('UTF-8').strip()
|
|
cfg = os.path.join(top_level, CONFIG)
|
|
if os.path.isfile(cfg):
|
|
pass
|
|
elif SKIP_ON_MISSING_CONFIG or os.getenv('PRE_COMMIT_ALLOW_NO_CONFIG'):
|
|
print(
|
|
'`{}` config file not found. '
|
|
'Skipping `pre-commit`.'.format(CONFIG),
|
|
)
|
|
raise EarlyExit()
|
|
else:
|
|
raise FatalError(
|
|
'No {} file was found\n'
|
|
'- To temporarily silence this, run '
|
|
'`PRE_COMMIT_ALLOW_NO_CONFIG=1 git ...`\n'
|
|
'- To permanently silence this, install pre-commit with the '
|
|
'--allow-missing-config option\n'
|
|
'- To uninstall pre-commit run '
|
|
'`pre-commit uninstall`'.format(CONFIG),
|
|
)
|
|
|
|
|
|
def _exe():
|
|
with open(os.devnull, 'wb') as devnull:
|
|
for exe in (INSTALL_PYTHON, sys.executable):
|
|
try:
|
|
if not subprocess.call(
|
|
(exe, '-c', 'import pre_commit.main'),
|
|
stdout=devnull, stderr=devnull,
|
|
):
|
|
return (exe, '-m', 'pre_commit.main', 'run')
|
|
except OSError:
|
|
pass
|
|
|
|
if distutils.spawn.find_executable('pre-commit'):
|
|
return ('pre-commit', 'run')
|
|
|
|
raise FatalError(
|
|
'`pre-commit` not found. Did you forget to activate your virtualenv?',
|
|
)
|
|
|
|
|
|
def _rev_exists(rev):
|
|
return not subprocess.call(('git', 'rev-list', '--quiet', rev))
|
|
|
|
|
|
def _pre_push(stdin):
|
|
remote = sys.argv[1]
|
|
|
|
opts = ()
|
|
for line in stdin.decode('UTF-8').splitlines():
|
|
_, local_sha, _, remote_sha = line.split()
|
|
if local_sha == Z40:
|
|
continue
|
|
elif remote_sha != Z40 and _rev_exists(remote_sha):
|
|
opts = ('--origin', local_sha, '--source', remote_sha)
|
|
else:
|
|
# ancestors not found in remote
|
|
ancestors = subprocess.check_output((
|
|
'git', 'rev-list', local_sha, '--topo-order', '--reverse',
|
|
'--not', '--remotes={}'.format(remote),
|
|
)).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
|
|
opts = ('--all-files',)
|
|
else:
|
|
cmd = ('git', 'rev-parse', '{}^'.format(first_ancestor))
|
|
source = subprocess.check_output(cmd).decode().strip()
|
|
opts = ('--origin', local_sha, '--source', source)
|
|
|
|
if opts:
|
|
return opts
|
|
else:
|
|
# An attempt to push an empty changeset
|
|
raise EarlyExit()
|
|
|
|
|
|
def _opts(stdin):
|
|
fns = {
|
|
'commit-msg': lambda _: ('--commit-msg-filename', sys.argv[1]),
|
|
'pre-commit': lambda _: (),
|
|
'pre-push': _pre_push,
|
|
}
|
|
stage = HOOK_TYPE.replace('pre-', '')
|
|
return ('--config', CONFIG, '--hook-stage', stage) + fns[HOOK_TYPE](stdin)
|
|
|
|
|
|
def main():
|
|
retv, stdin = _run_legacy()
|
|
try:
|
|
_validate_config()
|
|
return retv | subprocess.call(_exe() + _opts(stdin))
|
|
except EarlyExit:
|
|
return retv
|
|
except FatalError as e:
|
|
print(e.args[0])
|
|
return 1
|
|
|
|
|
|
if __name__ == '__main__':
|
|
exit(main())
|