#!/usr/bin/env python
|
|
"""
|
A git filter for automated manifest regeneration at "git add" time. See
|
http://progit.org/book/ch7-2.html for more info about git filters. This
|
script just echoes back what comes to it on stdin to stdout, but it also
|
regenerates a MANIFEST file as a side effect.
|
|
Add this script somewhere to your operating system's $PATH (executable as
|
``manifestgen.py``).
|
|
Then add the following to your ~/.gitconfig::
|
|
[filter "manifestgen"]
|
clean = manifestgen.py
|
smudge = cat
|
|
Then add a ``.gitattributes`` file to the root of your project something like
|
this:
|
|
* filter=manifestgen
|
|
Whenever you use ``git add`` in the project, the MANIFEST file at the project
|
root will be regenerated (and will include the file you've just added). This
|
will happen for each and every file added *except* MANIFEST itself.
|
|
The generated MANIFEST will have a comment at the top of it with the epoch
|
time it was generated.
|
|
Setting the OS environment variable DEBUG_MANIFESTGEN will send informational
|
output to stderr during its run.
|
"""
|
|
import os
|
import re
|
import sys
|
import time
|
|
try:
|
from subprocess import check_output, PIPE
|
except ImportError:
|
# BBB for python <2.7
|
def check_output(*popenargs, **kwargs):
|
from subprocess import CalledProcessError
|
from subprocess import Popen
|
if 'stdout' in kwargs:
|
raise ValueError(
|
'stdout argument not allowed, it will be overridden.')
|
process = Popen(stdout=PIPE, *popenargs, **kwargs)
|
output, unused_err = process.communicate()
|
retcode = process.poll()
|
if retcode:
|
cmd = kwargs.get("args")
|
if cmd is None:
|
cmd = popenargs[0]
|
raise CalledProcessError(retcode, cmd)
|
return output
|
|
def list_git_files(cwd):
|
# NB: passing the "-z" option to "git ls-files" below returns the
|
# output as a blob of null-terminated filenames without
|
# canonicalization or use of "-quoting.
|
#
|
# So we'll get back e.g.:
|
#
|
#'pyramid/tests/fixtures/static/h\xc3\xa9h\xc3\xa9.html'
|
#
|
# instead of:
|
#
|
#'"pyramid/tests/fixtures/static/h\\303\\251h\\303\\251.html"'
|
#
|
# for each file
|
git_top = check_output(['git', 'rev-parse', '--show-toplevel'],
|
stderr=PIPE, cwd=cwd).strip()
|
ls = check_output(['git', 'ls-files', '-z'], cwd=git_top, stderr=PIPE)
|
filenames = filter(None, ls.split('\x00')) # filter None for trailing \x00
|
return filenames
|
|
marker = '###--- generated by manifestgen at %s ---###\n'
|
marker_re = re.compile(marker % '\\d+')
|
manifest = 'MANIFEST'
|
|
def debug(msg):
|
if 'DEBUG_MANIFESTGEN' in os.environ:
|
sys.stderr.write('manifestgen: ' + msg + '\n')
|
sys.stderr.flush()
|
|
def main():
|
firstline = sys.stdin.readline()
|
sys.stdout.write(firstline)
|
for line in sys.stdin:
|
sys.stdout.write(line)
|
sys.stdout.flush()
|
if marker_re.match(firstline): # dont overwrite manifest when adding it
|
debug('not overwriting %s' % manifest)
|
else:
|
here = os.getcwd() # always root dir of project
|
new = '\n'.join(list_git_files(here)) + '\n'
|
mf = os.path.join(here, manifest)
|
try:
|
mfp = open(mf, 'r')
|
old = mfp.read()
|
except IOError:
|
old = ''
|
old = '\n'.join(old.split('\n')[1:])
|
if old == new:
|
debug('manifest NOT changed, NOT overwriting %s' % manifest)
|
else:
|
debug('manifest changed, overwriting %s' % manifest)
|
mfp = open(mf, 'w')
|
now = time.time()
|
mfp.write(marker % int(now))
|
mfp.write(new)
|
mfp.close()
|
|
if __name__ == '__main__':
|
main()
|