root/timingandestimationplugin/branches/trac0.11/scripts/trac-post-commit.py

Revision 3687, 10.1 kB (checked in by bobbysmith007, 5 months ago)

made commit hook regexes be case insensitive

Line 
1 #!/usr/bin/env python
2 #!/usr/bin/env python
3
4 # trac-post-commit-hook
5 # ----------------------------------------------------------------------------
6 # Copyright (c) 2004 Stephen Hansen
7 #
8 # Permission is hereby granted, free of charge, to any person obtaining a copy
9 # of this software and associated documentation files (the "Software"), to
10 # deal in the Software without restriction, including without limitation the
11 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 # sell copies of the Software, and to permit persons to whom the Software is
13 # furnished to do so, subject to the following conditions:
14 #
15 #   The above copyright notice and this permission notice shall be included in
16 #   all copies or substantial portions of the Software.
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 # IN THE SOFTWARE.
25 # ----------------------------------------------------------------------------
26
27
28
29
30 # Changes for the Timing and Estimation plugin
31 #
32 # "Blah refs #12 (1)" will add 1h to the spent time for issue #12
33 # "Blah refs #12 (spent 1.5)" will add 1h to the spent time for issue #12
34 #
35 # As above it is possible to use complicated messages:
36 #
37 # "Changed blah and foo to do this or that. Fixes #10 (1) and #12 (2), and refs #13 (0.5)."
38 #
39 # This will close #10 and #12, and add a note to #13 and also add 1h spent time to #10,
40 # add 2h spent time to #12 and add 30m spent time to #13.
41 #
42 # Note that:
43 #     (spent 2), (sp 2) or simply (2) may be used for spent
44 #     ' ', ',', '&' or 'and' may be used references
45 #
46
47
48
49
50 # This Subversion post-commit hook script is meant to interface to the
51 # Trac (http://www.edgewall.com/products/trac/) issue tracking/wiki/etc
52 # system.
53 #
54 # It should be called from the 'post-commit' script in Subversion, such as
55 # via:
56 #
57 # REPOS="$1"
58 # REV="$2"
59 #
60 # /usr/bin/python /usr/local/src/trac/contrib/trac-post-commit-hook \
61 #  -p "$TRAC_ENV" -r "$REV"
62 #
63 # (all the other arguments are now deprecated and not needed anymore)
64 #
65 # It searches commit messages for text in the form of:
66 #   command #1
67 #   command #1, #2
68 #   command #1 & #2
69 #   command #1 and #2
70 #
71 # Instead of the short-hand syntax "#1", "ticket:1" can be used as well, e.g.:
72 #   command ticket:1
73 #   command ticket:1, ticket:2
74 #   command ticket:1 & ticket:2
75 #   command ticket:1 and ticket:2
76 #
77 # In addition, the ':' character can be omitted and issue or bug can be used
78 # instead of ticket.
79 #
80 # You can have more then one command in a message. The following commands
81 # are supported. There is more then one spelling for each command, to make
82 # this as user-friendly as possible.
83 #
84 #   close, closed, closes, fix, fixed, fixes
85 #     The specified issue numbers are closed with the contents of this
86 #     commit message being added to it.
87 #   references, refs, addresses, re, see
88 #     The specified issue numbers are left in their current status, but
89 #     the contents of this commit message are added to their notes.
90 #
91 # A fairly complicated example of what you can do is with a commit message
92 # of:
93 #
94 #    Changed blah and foo to do this or that. Fixes #10 and #12, and refs #12.
95 #
96 # This will close #10 and #12, and add a note to #12.
97
98 import re
99 import os
100 import sys
101 from datetime import datetime
102
103 from trac.env import open_environment
104 from trac.ticket.notification import TicketNotifyEmail
105 from trac.ticket import Ticket
106 from trac.ticket.web_ui import TicketModule
107 # TODO: move grouped_changelog_entries to model.py
108 from trac.util.text import to_unicode
109 from trac.util.datefmt import utc
110 from trac.versioncontrol.api import NoSuchChangeset
111
112 logfile = "/var/trac/commithook.log"
113 LOG = False
114
115 if LOG:
116     f = open (logfile,"w")
117     f.write("Begin Log\n")
118     f.close()
119     def log (s, *params):
120         f = open (logfile,"a")
121         f.write(s % params)
122         f.write("\n")
123         f.close()
124 else:
125     def log (s, *params):
126         pass
127
128 from optparse import OptionParser
129
130 parser = OptionParser()
131 depr = '(not used anymore)'
132 parser.add_option('-e', '--require-envelope', dest='envelope', default='',
133                   help="""
134 Require commands to be enclosed in an envelope.
135 If -e[], then commands must be in the form of [closes #4].
136 Must be two characters.""")
137 parser.add_option('-p', '--project', dest='project',
138                   help='Path to the Trac project.')
139 parser.add_option('-r', '--revision', dest='rev',
140                   help='Repository revision number.')
141 parser.add_option('-u', '--user', dest='user',
142                   help='The user who is responsible for this action '+depr)
143 parser.add_option('-m', '--msg', dest='msg',
144                   help='The log message to search '+depr)
145 parser.add_option('-c', '--encoding', dest='encoding',
146                   help='The encoding used by the log message '+depr)
147 parser.add_option('-s', '--siteurl', dest='url',
148                   help=depr+' the base_url from trac.ini will always be used.')
149
150 (options, args) = parser.parse_args(sys.argv[1:])
151
152 _supported_cmds = {'close':      '_cmdClose',
153                    'closed':     '_cmdClose',
154                    'closes':     '_cmdClose',
155                    'fix':        '_cmdClose',
156                    'fixed':      '_cmdClose',
157                    'fixes':      '_cmdClose',
158                    'addresses':  '_cmdRefs',
159                    're':         '_cmdRefs',
160                    'references': '_cmdRefs',
161                    'refs':       '_cmdRefs',
162                    'see':        '_cmdRefs'}
163
164 ticket_prefix = '(?:#|(?:ticket|issue|bug)[: ]?)'
165 time_pattern = r'[ ]?(?:\((?:(?:spent|sp)[ ]?)?(-?[0-9]*(?:\.[0-9]+)?)\))?'
166 ticket_reference = ticket_prefix + '[0-9]+'+time_pattern
167 support_cmds_pattern = '|'.join(_supported_cmds.keys())
168 ticket_command =  (r'(?P<action>(?:%s))[ ]*'
169                    '(?P<ticket>%s(?:(?:[, &]*|[ ]?and[ ]?)%s)*)' %
170                    (support_cmds_pattern,ticket_reference, ticket_reference))
171 command_re = re.compile(ticket_command, re.IGNORECASE)
172 ticket_re = re.compile(ticket_prefix + '([0-9]+)', re.IGNORECASE)
173
174
175 if options.envelope:
176     ticket_command = r'\%s%s\%s' % (options.envelope[0], ticket_command,
177                                     options.envelope[1])
178    
179 command_re = re.compile(ticket_command, re.IGNORECASE)
180 ticket_re = re.compile(ticket_prefix + '([0-9]+)'+time_pattern, re.IGNORECASE)
181
182 class CommitHook:
183
184     def init_env(self, project):
185         self.env = open_environment(project)
186        
187
188     def __init__(self, project=options.project, author=options.user,
189                  rev=options.rev, url=options.url):
190         self.init_env( project )
191        
192         repos = self.env.get_repository()
193         repos.sync()
194         # Instead of bothering with the encoding, we'll use unicode data
195         # as provided by the Trac versioncontrol API (#1310).
196         try:
197             chgset = repos.get_changeset(rev)
198         except NoSuchChangeset:
199             return # out of scope changesets are not cached
200         self.author = chgset.author
201         self.rev = rev
202         self.msg = "(In [%s]) %s" % (rev, chgset.message)
203         self.now = datetime.now(utc)
204
205         cmd_groups = command_re.findall(self.msg)
206         log ("cmd_groups:%s", cmd_groups)
207         tickets = {}
208         for cmd, tkts, xxx1, xxx2 in cmd_groups:
209             log ("cmd:%s, tkts%s ", cmd, tkts)
210             funcname = _supported_cmds.get(cmd.lower(), '')
211             if funcname:
212                 for tkt_id, spent in ticket_re.findall(tkts):
213                     func = getattr(self, funcname)
214                     lst = tickets.setdefault(tkt_id, [])
215                     lst.append([func, spent])
216                    
217
218         for tkt_id, vals in tickets.iteritems():
219             log ("tkt_id:%s, vals%s ", tkt_id, vals)
220             spent_total = 0.0
221             try:
222                 db = self.env.get_db_cnx()
223                
224                 ticket = Ticket(self.env, int(tkt_id), db)
225                 for (cmd, spent) in vals:
226                     cmd(ticket)
227                     if spent:
228                         spent_total += float(spent)
229
230                 # determine sequence number...
231                 cnum = 0
232                 tm = TicketModule(self.env)
233                 for change in tm.grouped_changelog_entries(ticket, db):
234                     if change['permanent']:
235                         cnum += 1
236                 if spent_total:
237                     self._setTimeTrackerFields(ticket, spent_total)
238                 ticket.save_changes(self.author, self.msg, self.now, db, cnum+1)
239                 db.commit()
240
241                 tn = TicketNotifyEmail(self.env)
242                 tn.notify(ticket, newticket=0, modtime=self.now)
243             except Exception, e:
244                 # import traceback
245                 # traceback.print_exc(file=sys.stderr)
246                 log('Unexpected error while processing ticket ' \
247                                    'ID %s: %s' % (tkt_id, e))
248                 print>>sys.stderr, 'Unexpected error while processing ticket ' \
249                                    'ID %s: %s' % (tkt_id, e)
250            
251
252     def _cmdClose(self, ticket):
253         ticket['status'] = 'closed'
254         ticket['resolution'] = 'fixed'
255
256     def _cmdRefs(self, ticket):
257         pass
258
259     def _setTimeTrackerFields(self, ticket, spent):
260         log ("Setting ticket:%s spent: %s", ticket, spent)
261         if (spent != ''):
262             spentTime = float(spent)
263             if (ticket.values.has_key('hours')):
264                 ticket['hours'] = str(spentTime)
265                
266
267 if __name__ == "__main__":
268     if len(sys.argv) < 5:
269         print "For usage: %s --help" % (sys.argv[0])
270         print
271         print "Note that the deprecated options will be removed in Trac 0.12."
272     else:
273         CommitHook()
Note: See TracBrowser for help on using the browser.