-
Notifications
You must be signed in to change notification settings - Fork 21
/
update-hash
executable file
·137 lines (126 loc) · 4.89 KB
/
update-hash
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
#!/usr/bin/env python3
"""
Helper script to update buildout.hash.cfg
Usage:
$ ./update-hash [BUILDOUT_HASH_CFG]
Automatic installation using husky, from the root of the repository:
$ npm install
Manual installation:
$ cp update-hash .git/hooks/
$ $EDITOR .git/hooks/pre-commit
#!/bin/bash
set -e
touch "$(git rev-parse --git-dir)/hooks/.need-post-commit"
$ $EDITOR .git/hooks/post-commit
#!/bin/bash
set -e
MARKER="$(git rev-parse --git-dir)/hooks/.need-post-commit"
UPDATE_HASH="$(git rev-parse --git-dir)/hooks/update-hash"
if [ -e "$MARKER" -a -x "$UPDATE_HASH" ]; then
rm "$MARKER"
if git diff-index --quiet HEAD -- ; then
# nothing
true
else
git stash save --keep-index --quiet "update-hash pre-commit hook"
trap "git stash pop" EXIT
fi
find "$(git rev-parse --show-toplevel)" -name "buildout.hash.cfg" | while IFS= read -r HASHFILE; do
"$UPDATE_HASH" "$HASHFILE"
git add "$HASHFILE"
done
git commit --amend --no-verify -C HEAD
fi
BEWARE: rebasing does not trigger this hook, so you have to commit each
change explicitely. Improvements welcome.
"""
import hashlib
import os
import shutil
import sys
# Note: this is an intentionally very restrictive and primitive
# ConfigParser-ish parser.
# buildout.hash.cfg files are ConfigParser-compatible, but they are *not*
# ConfigParser syntax in order to be strictly validated, to prevent misuse
# and allow easy extension (ex: to other hashes).
FILENAME_KEY_LIST = ['filename', '_update_hash_filename_']
HASH_MAP = {
'md5sum': hashlib.md5,
}
def main():
for infile_path in sys.argv[1:] or ['buildout.hash.cfg']:
eol = None
hash_file_path = None
hash_name = None
current_section = None
infile_dirname = os.path.dirname(infile_path)
outfile_path = infile_path + '.tmp'
infile = open(infile_path, 'r')
outfile_fd = os.open(outfile_path, os.O_EXCL | os.O_CREAT | os.O_WRONLY)
try:
outfile = os.fdopen(outfile_fd, 'w')
write = outfile.write
if sys.version_info <= (3,):
nextLine = iter(infile).next
else:
nextLine = iter(infile).__next__
while True:
try:
line = nextLine()
except StopIteration:
line = None
if line is None or not line.startswith('#'):
if line is None or line.startswith('['):
if hash_file_path is not None:
current_section.insert(
len([x for x in current_section if x.strip()]),
'%s = %s%s' % (
hash_name,
HASH_MAP[hash_name](
open(
os.path.join(
infile_dirname,
*hash_file_path.split('/')
),
'rb',
).read()
).hexdigest(),
eol,
),
)
outfile.writelines(current_section)
hash_file_path = hash_name = None
if line is None:
break
hash_file_path, _ = line[1:].split(']', 1)
current_section = []
elif '=' in line:
assert current_section is not None, line
name, value = line.split('=', 1)
name = name.strip()
value = value.strip()
if name in FILENAME_KEY_LIST:
hash_file_path = value
current_section.append(line)
else:
for hash_name in HASH_MAP:
if name == hash_name:
break
else:
raise ValueError('Unknown key: %r' % (name, ))
# NOT appending this line, it will be re-generated from
# scratch
eol = ''.join(x for x in line if x in ('\r', '\n'))
continue
if current_section is None:
write(line)
else:
current_section.append(line)
outfile.close()
shutil.copymode(infile_path, outfile_path)
shutil.move(outfile_path, infile_path)
except Exception:
os.unlink(outfile_path)
raise
if __name__ == '__main__':
main()