commit | author | age
|
b4ca9f
|
1 |
#!/usr/bin/env python |
CM |
2 |
|
|
3 |
""" |
|
4 |
A git filter for automated manifest regeneration at "git add" time. See |
|
5 |
http://progit.org/book/ch7-2.html for more info about git filters. This |
|
6 |
script just echoes back what comes to it on stdin to stdout, but it also |
10d85b
|
7 |
regenerates a MANIFEST file as a side effect. |
b4ca9f
|
8 |
|
CM |
9 |
Add this script somewhere to your operating system's $PATH (executable as |
|
10 |
``manifestgen.py``). |
|
11 |
|
|
12 |
Then add the following to your ~/.gitconfig:: |
|
13 |
|
|
14 |
[filter "manifestgen"] |
|
15 |
clean = manifestgen.py |
|
16 |
smudge = cat |
|
17 |
|
|
18 |
Then add a ``.gitattributes`` file to the root of your project something like |
|
19 |
this: |
|
20 |
|
|
21 |
* filter=manifestgen |
|
22 |
|
10d85b
|
23 |
Whenever you use ``git add`` in the project, the MANIFEST file at the project |
CM |
24 |
root will be regenerated (and will include the file you've just added). This |
|
25 |
will happen for each and every file added *except* MANIFEST itself. |
b4ca9f
|
26 |
|
10d85b
|
27 |
The generated MANIFEST will have a comment at the top of it with the epoch |
b4ca9f
|
28 |
time it was generated. |
CM |
29 |
|
|
30 |
Setting the OS environment variable DEBUG_MANIFESTGEN will send informational |
|
31 |
output to stderr during its run. |
|
32 |
""" |
|
33 |
|
|
34 |
import os |
|
35 |
import re |
|
36 |
import sys |
|
37 |
import time |
|
38 |
|
|
39 |
try: |
|
40 |
from subprocess import check_output, PIPE |
|
41 |
except ImportError: |
|
42 |
# BBB for python <2.7 |
|
43 |
def check_output(*popenargs, **kwargs): |
|
44 |
from subprocess import CalledProcessError |
|
45 |
from subprocess import Popen |
|
46 |
if 'stdout' in kwargs: |
|
47 |
raise ValueError( |
|
48 |
'stdout argument not allowed, it will be overridden.') |
|
49 |
process = Popen(stdout=PIPE, *popenargs, **kwargs) |
|
50 |
output, unused_err = process.communicate() |
|
51 |
retcode = process.poll() |
|
52 |
if retcode: |
|
53 |
cmd = kwargs.get("args") |
|
54 |
if cmd is None: |
|
55 |
cmd = popenargs[0] |
|
56 |
raise CalledProcessError(retcode, cmd) |
|
57 |
return output |
|
58 |
|
|
59 |
def list_git_files(cwd): |
|
60 |
# NB: passing the "-z" option to "git ls-files" below returns the |
|
61 |
# output as a blob of null-terminated filenames without |
|
62 |
# canonicalization or use of "-quoting. |
|
63 |
# |
|
64 |
# So we'll get back e.g.: |
|
65 |
# |
|
66 |
#'pyramid/tests/fixtures/static/h\xc3\xa9h\xc3\xa9.html' |
|
67 |
# |
|
68 |
# instead of: |
|
69 |
# |
|
70 |
#'"pyramid/tests/fixtures/static/h\\303\\251h\\303\\251.html"' |
|
71 |
# |
|
72 |
# for each file |
|
73 |
git_top = check_output(['git', 'rev-parse', '--show-toplevel'], |
|
74 |
stderr=PIPE, cwd=cwd).strip() |
|
75 |
ls = check_output(['git', 'ls-files', '-z'], cwd=git_top, stderr=PIPE) |
|
76 |
filenames = filter(None, ls.split('\x00')) # filter None for trailing \x00 |
|
77 |
return filenames |
|
78 |
|
|
79 |
marker = '###--- generated by manifestgen at %s ---###\n' |
|
80 |
marker_re = re.compile(marker % '\\d+') |
10d85b
|
81 |
manifest = 'MANIFEST' |
b4ca9f
|
82 |
|
CM |
83 |
def debug(msg): |
|
84 |
if 'DEBUG_MANIFESTGEN' in os.environ: |
|
85 |
sys.stderr.write('manifestgen: ' + msg + '\n') |
|
86 |
sys.stderr.flush() |
|
87 |
|
|
88 |
def main(): |
|
89 |
firstline = sys.stdin.readline() |
|
90 |
sys.stdout.write(firstline) |
|
91 |
for line in sys.stdin: |
|
92 |
sys.stdout.write(line) |
|
93 |
sys.stdout.flush() |
|
94 |
if marker_re.match(firstline): # dont overwrite manifest when adding it |
|
95 |
debug('not overwriting %s' % manifest) |
|
96 |
else: |
|
97 |
here = os.getcwd() # always root dir of project |
b5a1fc
|
98 |
new = '\n'.join(list_git_files(here)) + '\n' |
b4ca9f
|
99 |
mf = os.path.join(here, manifest) |
bef31f
|
100 |
try: |
CM |
101 |
mfp = open(mf, 'r') |
|
102 |
old = mfp.read() |
|
103 |
except IOError: |
|
104 |
old = '' |
|
105 |
old = '\n'.join(old.split('\n')[1:]) |
|
106 |
if old == new: |
|
107 |
debug('manifest NOT changed, NOT overwriting %s' % manifest) |
|
108 |
else: |
|
109 |
debug('manifest changed, overwriting %s' % manifest) |
|
110 |
mfp = open(mf, 'w') |
|
111 |
now = time.time() |
|
112 |
mfp.write(marker % int(now)) |
|
113 |
mfp.write(new) |
b4ca9f
|
114 |
mfp.close() |
CM |
115 |
|
|
116 |
if __name__ == '__main__': |
|
117 |
main() |