import re
import os
import sys
import time
from trac.env import open_environment
from trac.Notify import TicketNotifyEmail
from trac.ticket import Ticket
from trac.web.href import Href
try:
from optparse import OptionParser
except ImportError:
try:
from optik import OptionParser
except ImportError:
raise ImportError, 'Requires Python 2.3 or the Optik option parsing library.'
parser = OptionParser()
parser.add_option('-e', '--require-envelope', dest='env', default='',
help='Require commands to be enclosed in an envelope. If -e[], '
'then commands must be in the form of [closes #4]. Must '
'be two characters.')
parser.add_option('-p', '--project', dest='project',
help='Path to the Trac project.')
parser.add_option('-r', '--revision', dest='rev',
help='Repository revision number.')
parser.add_option('-u', '--user', dest='user',
help='The user who is responsible for this action')
parser.add_option('-m', '--msg', dest='msg',
help='The log message to search.')
parser.add_option('-s', '--siteurl', dest='url',
help='The base URL to the project\'s trac website (to which '
'/ticket/## is appended). If this is not specified, '
'the project URL from trac.ini will be used.')
(options, args) = parser.parse_args(sys.argv[1:])
if options.env:
leftEnv = '\\' + options.env[0]
rghtEnv = '\\' + options.env[1]
else:
leftEnv = ''
rghtEnv = ''
commandPattern = re.compile(leftEnv + r'(?P<action>[A-Za-z]*).?(?P<ticket>#[0-9]+(?:(?:[, &]*|[ ]?and[ ]?)#[0-9]+)*)' + rghtEnv)
ticketPattern = re.compile(r'#([0-9]*)')
class CommitHook:
_supported_cmds = {'close': '_cmdClose',
'closed': '_cmdClose',
'closes': '_cmdClose',
'fix': '_cmdClose',
'fixed': '_cmdClose',
'fixes': '_cmdClose',
'adresses': '_cmdRefs',
'addresses': '_cmdRefs',
're': '_cmdRefs',
'references': '_cmdRefs',
'refs': '_cmdRefs',
'see': '_cmdRefs'}
def __init__(self, project=options.project, author=options.user,
rev=options.rev, msg=options.msg, url=options.url):
self.author = author
self.rev = rev
self.msg = "(In [changeset:%s changeset %s]) %s" % (rev, rev, msg)
self.now = int(time.time())
self.env = open_environment(project)
if url is None:
url = self.env.config.get('project', 'url')
self.env.href = Href(url)
self.env.abs_href = Href(url)
cmdGroups = commandPattern.findall(msg)
for cmd, tkts in cmdGroups:
if CommitHook._supported_cmds.has_key(cmd.lower()):
func = getattr(self, CommitHook._supported_cmds[cmd.lower()])
func(ticketPattern.findall(tkts))
def _cmdClose(self, tickets):
for tkt_id in tickets:
try:
ticket = Ticket(self.env, tkt_id)
ticket['owner'] = self.author
ticket['status'] = 'closed'
ticket['resolution'] = 'fixed'
ticket.save_changes(self.author, self.msg, self.now)
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=0, modtime=self.now)
except Exception, e:
print>>sys.stderr, 'Unexpected error while processing ticket ' \
'ID %s: %s' % (tkt_id, e)
def _cmdRefs(self, tickets):
for tkt_id in tickets:
try:
ticket = Ticket(self.env, tkt_id)
ticket.save_changes(self.author, self.msg, self.now)
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=0, modtime=self.now)
except Exception, e:
print>>sys.stderr, 'Unexpected error while processing ticket ' \
'ID %s: %s' % (tkt_id, e)
if __name__ == "__main__":
if len(sys.argv) < 5:
print "For usage: %s --help" % (sys.argv[0])
else:
CommitHook()