forked from pre-commit/pre-commit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhook-tmpl
More file actions
executable file
·195 lines (160 loc) · 5.77 KB
/
hook-tmpl
File metadata and controls
executable file
·195 lines (160 loc) · 5.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/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 = {
'prepare-commit-msg': lambda _: ('--commit-msg-filename', sys.argv[1]),
'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)
if sys.version_info < (3, 7): # https://bugs.python.org/issue25942
def _subprocess_call(cmd): # this is the python 2.7 implementation
return subprocess.Popen(cmd).wait()
else:
_subprocess_call = subprocess.call
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
except KeyboardInterrupt:
return 1
if __name__ == '__main__':
exit(main())