Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 146 lines (114 sloc) 4.31 KB
#!/usr/bin/python
import sys
import re
import os
import subprocess
def process(commit_msg_path):
"""
Process the commit message. If this method returns,
it is assumed the message has been validated. An
Exception is the best way to exit in case of error.
"""
# read in commit message
commit_msg_file = open(commit_msg_path, 'r')
commit_msg = commit_msg_file.read()
commit_msg_file.close()
# check to see if message is properly formatted
valid = validate_message(commit_msg)
if valid and len(valid) == 2:
new_commit_msg = valid[1]
else:
# present sprint.ly items to user
display_sprintly_items()
# prompt user for item(s)
items = get_sprintly_items()
# check if they opted out
if '0' in items:
print 'Proceeding without Sprint.ly item number.'
return
# convert items into string
items_string = ' '.join(map(lambda x: 'Re #' + str(x) + '.', items))
# create new commit message
new_commit_msg = items_string + ' ' + commit_msg
# save it (overwrite existing)
commit_msg_file = open(commit_msg_path, 'w')
commit_msg = commit_msg_file.write(new_commit_msg)
commit_msg_file.close()
def validate_message(message):
"""
If the message contains (at any position) a sprint.ly
keyword followed by a space, pound, number, then accept
the message as is and return (True, message).
If the message begins with a pound, number, prepend
message with 'References ' and return (True, modified message).
Otherwise, return false.
"""
messageLower = message.lower()
valid_keywords = ['close', 'closes', 'closed', 'fix', 'fixed', 'fixes', 'addresses', 're', 'ref', 'refs', 'references', 'see', 'breaks', 'unfixes', 'reopen', 'reopens', 're-open', 're-opens']
try:
# match pound-number-(space or period)
result = re.match(r'^(#[0-9]+[\.\s$]).*$', message)
if result:
return (True, 'References %s' % message)
# match any keyword followed by a pound-number-(space or period)
pattern = r'.*\b(' + '|'.join(valid_keywords) + r')\b\s(#[0-9]+([\.\s]|$)).*'
result = re.match(pattern, messageLower)
if result:
return (True, message)
except Exception as e:
pass
return False
def display_sprintly_items():
"""
Use the sprintly command line tool to display a list of sprintly
items.
"""
try:
subprocess.call(['sprintly'], stdin=open('/dev/tty', 'r'))
except:
print 'Command-line tool \'sprintly\' not found. Please ensure it is installed and on your path.'
print '#0 - Proceed without Sprint.ly item number.'
def get_sprintly_items():
"""
Ask the user until they give a list of one or more
integers delimited by space. Only non-negative
integers are allowed. It is acceptable for integers
to be preceded by a # symbol.
"""
# enable user input
sys.stdin = open('/dev/tty', 'r')
while True:
sprintly_items = raw_input('Enter 1 or more item numbers separated by a space: ').split(' ')
result = map(lambda x: parse_item_number(x), sprintly_items)
if not None in result:
return result
def parse_item_number(s):
"""
Returns the item number from strings of format: '12', '#12'
"""
result = re.match('^#?([0-9]+)$', s)
if result:
return result.group(1)
else:
return None
if __name__ == '__main__':
try:
if len(sys.argv) > 1:
process(commit_msg_path=sys.argv[1])
else:
# Should never happen, but just in case...
raise Exception('Commit message was not received.')
except KeyboardInterrupt:
print '\n\nProgram interrupted. Commit aborted.'
sys.exit(1)
except Exception as e:
print '\n\nError occurred. Commit aborted.'
sys.exit(1)
# Execute the original commit hook. Note: We don't want realpath because
# sprintly links /usr/local/share/sprintly/commit-msg to
# .git/hooks/commit-msg and we want to be in the hooks directory.
original_commit_msg = os.path.dirname(__file__) + '/commit-msg.original'
if os.path.exists(original_commit_msg):
sys.exit(subprocess.call([original_commit_msg, sys.argv[1]]))
else:
sys.exit(0)