#!/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)
if props.get('svn:special'):
sys.stderr.write("file %s has svn:special flag, probably a symlink. don't check other props." % ff)
continue
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())
Professional programmer; amateur home handyman (on our home only); tinkerer; husband; father of 3; attempting to be a renaissance guy (to know at least a little about a lot of subjects, a doomed pursuit in an information age); geek-arts-and-sciences enthusiast. Interest areas: Science fiction, wind turbines, electric cars, renewable energy, making things.
Friday, May 10, 2013
Subversion pre-commit hook script - Python files: prevent tabs, verify svn properties
Here's a precommit hook script I've modified from one I've used before. I hope it comes in handy. I'm going to try to submit it to the dev group of subversion itself for inclusion in the contrib/hook-scripts directory.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment