Index: contrib/hook-scripts/precommit-pyfiles-notabs-properties.py =================================================================== --- contrib/hook-scripts/precommit-pyfiles-notabs-properties.py (revision 0) +++ contrib/hook-scripts/precommit-pyfiles-notabs-properties.py (working copy) @@ -0,0 +1,127 @@ +#!/bin/env python + +""" + pre-commit hook script that does several things: + * prevents committing any python file containing a tab character. + * checks if there are tabs in the source file and warns if so; + * aborts if incorrect properties of eol-style and keywords 'id'. +""" +import sys +import os +import traceback +from optparse import OptionParser + +#sys.stderr.write("NOTE: pre-commit hook script enabled - checks for tabs, svn eol-style and id properties...\n") + +def command_output(cmd): + " Capture a command's standard output. " + import subprocess + return subprocess.Popen( + cmd.split(), stdout=subprocess.PIPE).communicate()[0] + +def files_changed(look_cmd): + """ List the files added or updated by this transaction. + + "svnlook changed" gives output like: + U trunk/file1.cpp + A trunk/file2.py + """ + def filename(line): + return line[4:] + + def added_or_updated(line): + return line and line[0] in ("A", "U") + + retval = [] + for line in command_output(look_cmd % "changed").split("\n"): + if added_or_updated(line): + retval.append(filename(line)) + #sys.stderr.write("files changed: %s" % (retval)) + return retval + +def file_contents(filename, look_cmd): + " Return a file's contents for this transaction. " + return command_output("%s %s" % (look_cmd % "cat", filename)) + +def file_get_properties(filename, look_cmd): + propslines = command_output("%s %s" % (look_cmd % "proplist -v", filename)) + res = {} + for line in propslines.split('\n'): + line = line.strip() + if not line: + continue + k, v = line.split(' : ') + res[k] = v + return res + +def contains_tabs(filename, look_cmd): + " Return True if this version of the file contains tabs. " + return "\t" in file_contents(filename, look_cmd) + +def check_py_files(look_cmd): + " Check Python files in this transaction are tab-free. " + + def is_py_file(fname): + return os.path.splitext(fname)[1] == ".py" + + py_files_with_tabs = set() + py_files_bad_eolstyle = set() + py_files_bad_exec = set() + py_files_bad_keywords = set() + for ff in files_changed(look_cmd): + if not is_py_file(ff): + continue + if contains_tabs(ff, look_cmd): + py_files_with_tabs.add(ff) + props = file_get_properties(ff, look_cmd) + eolstyle = props.get('svn:eol-style') + #sys.stderr.write("props: %s\neolstyle: '%s'\n" % (props, eolstyle)) + if eolstyle in ('native', 'LFFFFF'): + py_files_bad_eolstyle.add(ff) + execut = props.get('svn:executable') + if execut not in ['ON', '*']: + py_files_bad_exec.add(ff) + keywords = props.get('svn:keywords') + if (not keywords) or ('Id' not in keywords.split()): + py_files_bad_keywords.add(ff) + + preventCommit = False + if len(py_files_with_tabs) > 0: + sys.stderr.write("The following files contain tabs:\n%s\n" % "\n".join(py_files_with_tabs)) + preventCommit = True + if len(py_files_bad_exec) > 0: + sys.stderr.write("The following py files are missing 'executable' property, committing anyway, but please fix this:\n%s\n" % "\n".join(py_files_bad_exec)) + # note, do not prevent commit over this, just warn. + if len(py_files_bad_keywords) > 0: + sys.stderr.write("The following files don't have keywords property set to 'Id' at least. Please fix this before committing:\n%s\n" % "\n".join(py_files_bad_keywords)) + preventCommit = True + if len(py_files_bad_eolstyle) > 0: + sys.stderr.write("The following files don't have svn propset svn:eol-style 'LF', please do so before committing:\n%s\n" % "\n".join(py_files_bad_eolstyle)) + preventCommit = True + + return preventCommit + +def main(): + usage = """usage: %prog REPOS TXN + Run pre-commit options on a repository transaction.""" + + parser = OptionParser(usage=usage) + parser.add_option("-r", "--revision", + help="Test mode. TXN actually refers to a revision.", + action="store_true", default=False) + errors = 0 + try: + (opts, (repos, txn_or_rvn)) = parser.parse_args() + look_opt = ("--transaction", "--revision")[opts.revision] + look_cmd = "svnlook %s %s %s %s" % ( + "%s", repos, look_opt, txn_or_rvn) + errors += check_py_files(look_cmd) + except: + parser.print_help() + errors += 1 + sys.stderr.write("Pre-commit hook traceback: %s" % (traceback.format_exc())) + return errors + +if __name__ == "__main__": + sys.exit(main()) + Property changes on: contrib/hook-scripts/precommit-pyfiles-notabs-properties.py ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +LF \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property