-
Notifications
You must be signed in to change notification settings - Fork 3
/
pre-commit.py
executable file
·108 lines (95 loc) · 3.29 KB
/
pre-commit.py
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
#!/usr/bin/env python
#
# This pre-commit hook builds and tests the project. If anything went wrong,
# then we're not going to commit.
#
# To enable this git hook, run the command
#
# ln -s ../../pre-commit.py .git/hooks/pre-commit
import sys
import subprocess
import os
# import shutil
from distutils.spawn import find_executable
root_dir = subprocess.check_output(['git', 'rev-parse' ,'--show-toplevel']).decode('utf-8').strip()
sys.path.append(root_dir)
sys.dont_write_bytecode = True
import build
def print_msg(msg):
print('\n\t{}\n'.format(msg))
def get_changed_files():
os.chdir(root_dir)
return subprocess.check_output(['git', 'diff-index', '--cached', '--name-only', 'HEAD']).decode('utf-8').splitlines()
class GitStasher(object):
"""Pushes and pops unstaged changes in a Python "with" block"""
def __init__(self):
super(GitStasher, self).__init__()
def __enter__(self):
try:
os.chdir(root_dir)
subprocess.check_call(['git', 'stash', 'save', '--keep-index', '--quiet'])
except subprocess.CalledProcessError as e:
print_msg('Failed to temporarily stash unstaged changes!\n\t(return code {})'.format(str(e.returncode)))
raise Exception('Fatal error.')
def __exit__(self, exception_type, exception_value, traceback):
try:
os.chdir(root_dir)
subprocess.check_call(['git', 'stash', 'pop', '--quiet'])
except subprocess.CalledProcessError as e:
print_msg('Failed to pop unstaged changes!\n\t(return code {})'.format(str(e.returncode)))
except Exception as e:
print_msg('Failed to pop unstaged changes! (Unknown exception)')
def do_clang_format():
if not find_executable('clang-format'):
print_msg('clang-format not present, skipping formatting.')
return
os.chdir(root_dir)
for file in get_changed_files():
if not file.endswith(('.h','.c', '.hh', '.cc', '.hpp', '.hpp', '.hxx', '.cxx')):
continue
try:
print_msg('Formatting "{}"'.format(file))
subprocess.check_call(['clang-format', '-style=file', '-i', file])
except Exception as e:
print_msg('Failed formatting, but continuing on.')
def do_build():
# Run clang-format on the changed files, if present.
print_msg('Building and testing the project.')
b = build.CommandLineBuild(0)
if b.before_build():
b.after_failure()
return 1
elif b.build():
b.after_failure()
return 2
elif b.after_build():
b.after_failure()
return 3
elif b.before_test():
b.after_failure()
return 4
elif b.test():
b.after_failure()
return 5
elif b.after_test():
b.after_failure()
return 6
else:
b.after_success()
return 0
def main():
exit_status = 0
try:
do_clang_format()
with GitStasher() as stash:
exit_status = do_build()
except build.SystemCallError as e:
exit_status = -1
except Exception as e:
print(str(e))
exit_status = -2
if exit_status != 0:
print_msg('There were build and/or test errors.\n\tPlease fix them and then try committing again.')
return exit_status
if __name__ == '__main__':
exit(main())