Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 21 additions & 25 deletions pre_commit/commands/install_uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import io
import os.path
import pipes
import sys

from pre_commit import output
Expand All @@ -21,6 +20,8 @@
'e358c9dae00eac5d06b38dfdb1e33a8c',
)
CURRENT_HASH = '138fd403232d2ddd5efb44317e38bf03'
TEMPLATE_START = '# start templated\n'
TEMPLATE_END = '# end templated\n'


def is_our_script(filename):
Expand Down Expand Up @@ -50,32 +51,27 @@ def install(
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,
),
'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)
params = {
'CONFIG': runner.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:
with io.open(resource_filename('hook-tmpl')) as f:
contents = f.read()
before, rest = contents.split(TEMPLATE_START)
to_template, after = rest.split(TEMPLATE_END)

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))
Expand Down
1 change: 0 additions & 1 deletion pre_commit/resources/commit-msg-tmpl

This file was deleted.

222 changes: 167 additions & 55 deletions pre_commit/resources/hook-tmpl
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,55 +1,167 @@
#!/usr/bin/env bash
# This is a randomish md5 to identify this script
# 138fd403232d2ddd5efb44317e38bf03

pushd "$(dirname "$0")" >& /dev/null
HERE="$(pwd)"
popd >& /dev/null

retv=0
args=""

ENV_PYTHON={sys_executable}
SKIP_ON_MISSING_CONF={skip_on_missing_conf}

if which pre-commit >& /dev/null; then
exe="pre-commit"
run_args=""
elif "$ENV_PYTHON" -c 'import pre_commit.main' >& /dev/null; then
exe="$ENV_PYTHON"
run_args="-m pre_commit.main"
elif python -c 'import pre_commit.main' >& /dev/null; then
exe="python"
run_args="-m pre_commit.main"
else
echo '`pre-commit` not found. Did you forget to activate your virtualenv?'
exit 1
fi

# Run the legacy pre-commit if it exists
if [ -x "$HERE"/{hook_type}.legacy ] && ! "$HERE"/{hook_type}.legacy "$@"; then
retv=1
fi

CONF_FILE="$(git rev-parse --show-toplevel)/{config_file}"
if [ ! -f "$CONF_FILE" ]; then
if [ "$SKIP_ON_MISSING_CONF" = true -o ! -z "$PRE_COMMIT_ALLOW_NO_CONFIG" ]; then
echo '`{config_file}` config file not found. Skipping `pre-commit`.'
exit $retv
else
echo 'No {config_file} file was found'
echo '- To temporarily silence this, run `PRE_COMMIT_ALLOW_NO_CONFIG=1 git ...`'
echo '- To permanently silence this, install pre-commit with the `--allow-missing-config` option'
echo '- To uninstall pre-commit run `pre-commit uninstall`'
exit 1
fi
fi

{hook_specific}

# Run pre-commit
if ! "$exe" $run_args run $args --config {config_file}; then
retv=1
fi

exit $retv
#!/usr/bin/env python
"""File generated by pre-commit: https://pre-commit.com"""
from __future__ import print_function

import distutils.spawn
import os
import subprocess
import sys

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 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 _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:
opts = ('--origin', local_sha, '--source', remote_sha)
else:
# First ancestor not found in remote
first_ancestor = subprocess.check_output((
'git', 'rev-list', '--max-count=1', '--topo-order',
'--reverse', local_sha, '--not', '--remotes={}'.format(remote),
)).decode().strip()
if not first_ancestor:
continue
else:
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())
31 changes: 0 additions & 31 deletions pre_commit/resources/pre-push-tmpl

This file was deleted.

Loading