3 # trac-post-commit-hook
4 # ----------------------------------------------------------------------------
5 # Copyright (c) 2004 Stephen Hansen
7 # Permission is hereby granted, free of charge, to any person obtaining a copy
8 # of this software and associated documentation files (the "Software"), to
9 # deal in the Software without restriction, including without limitation the
10 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 # sell copies of the Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 # ----------------------------------------------------------------------------
26 # This Subversion post-commit hook script is meant to interface to the
27 # Trac (http://www.edgewall.com/products/trac/) issue tracking/wiki/etc
30 # It should be called from the 'post-commit' script in Subversion, such as
35 # TRAC_ENV='/somewhere/trac/project/'
37 # /usr/bin/python /usr/local/src/trac/contrib/trac-post-commit-hook \
41 # It searches commit messages for text in the form of:
47 # You can have more then one command in a message. The following commands
48 # are supported. There is more then one spelling for each command, to make
49 # this as user-friendly as possible.
52 # The specified issue numbers are closed with the contents of this
53 # commit message being added to it.
54 # references, refs, addresses, re
55 # The specified issue numbers are left in their current status, but
56 # the contents of this commit message are added to their notes.
58 # A fairly complicated example of what you can do is with a commit message
61 # Changed blah and foo to do this or that. Fixes #10 and #12, and refs #12.
63 # This will close #10 and #12, and add a note to #12.
68 from datetime import datetime
70 from trac.env import open_environment
71 from trac.ticket.notification import TicketNotifyEmail
72 from trac.ticket import Ticket
73 from trac.ticket.web_ui import TicketModule
74 # TODO: move grouped_changelog_entries to model.py
75 from trac.util.datefmt import utc
76 from trac.versioncontrol.api import NoSuchChangeset
78 from optparse import OptionParser
80 parser = OptionParser()
81 parser.add_option('-p', '--project', dest='project', help='Path to the Trac project.')
82 parser.add_option('-r', '--revision', dest='rev', help='Repository revision number.')
84 (options, args) = parser.parse_args(sys.argv[1:])
88 commandPattern = re.compile(leftEnv + r'(?P<action>[A-Za-z]*).?(?P<ticket>#[0-9]+(?:(?:[, &]*|[ ]?and[ ]?)#[0-9]+)*)' + rghtEnv)
89 ticketPattern = re.compile(r'#([0-9]*)')
92 # 'close': '_cmdClose',
93 # 'closed': '_cmdClose',
94 # 'closes': '_cmdClose',
96 # 'fixed': '_cmdClose',
97 # 'fixes': '_cmdClose',
98 # 'addresses': '_cmdRefs',
99 # 'references': '_cmdRefs',
110 def __init__(self, project=options.project, rev=options.rev):
111 self.env = open_environment(project)
112 repos = self.env.get_repository()
115 # Instead of bothering with the encoding, we'll use unicode data
116 # as provided by the Trac versioncontrol API (#1310).
118 chgset = repos.get_changeset(rev)
119 except NoSuchChangeset:
120 return # out of scope changesets are not cached
121 self.author = chgset.author
123 self.msg = "(In [%s]) %s" % (rev, chgset.message)
124 self.now = datetime.now(utc)
126 cmdGroups = commandPattern.findall(self.msg)
129 for cmd, tkts in cmdGroups:
130 funcname = CommitHook._supported_cmds.get(cmd.lower(), '')
132 for tkt_id in ticketPattern.findall(tkts):
133 func = getattr(self, funcname)
134 tickets.setdefault(tkt_id, []).append(func)
136 for tkt_id, cmds in tickets.iteritems():
138 db = self.env.get_db_cnx()
140 ticket = Ticket(self.env, int(tkt_id), db)
144 # determine sequence number...
146 tm = TicketModule(self.env)
147 for change in tm.grouped_changelog_entries(ticket, db):
148 if change['permanent']:
151 ticket.save_changes(self.author, self.msg, self.now, db, cnum+1)
154 tn = TicketNotifyEmail(self.env)
155 tn.notify(ticket, newticket=0, modtime=self.now)
158 # traceback.print_exc(file=sys.stderr)
159 print>>sys.stderr, 'Unexpected error while processing ticket ' \
160 'ID %s: %s' % (tkt_id, e)
162 def _cmdClose(self, ticket):
163 ticket['status'] = 'closed'
164 ticket['resolution'] = 'fixed'
166 def _cmdRefs(self, ticket):
169 def _cmdQa(self, ticket):
170 ticket['phase'] = 'Initial QA'
172 ticket['status'] = 'new'
174 if __name__ == "__main__":
175 if len(sys.argv) < 5:
176 print "For usage: %s --help" % (sys.argv[0])
178 print "Note that the deprecated options will be removed in Trac 0.12."