Mercurial > genshi > genshi-test
comparison examples/trac/contrib/trac-post-commit-hook @ 39:71ecbe90aafc
Copy Trac to main branch.
author | cmlenz |
---|---|
date | Mon, 03 Jul 2006 18:53:27 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
38:fec9f4897415 | 39:71ecbe90aafc |
---|---|
1 #!/usr/bin/env python | |
2 | |
3 # trac-post-commit-hook | |
4 # ---------------------------------------------------------------------------- | |
5 # Copyright (c) 2004 Stephen Hansen | |
6 # | |
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: | |
13 # | |
14 # The above copyright notice and this permission notice shall be included in | |
15 # all copies or substantial portions of the Software. | |
16 # | |
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 | |
23 # IN THE SOFTWARE. | |
24 # ---------------------------------------------------------------------------- | |
25 | |
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 | |
28 # system. | |
29 # | |
30 # It should be called from the 'post-commit' script in Subversion, such as | |
31 # via: | |
32 # | |
33 # REPOS="$1" | |
34 # REV="$2" | |
35 # LOG=`/usr/bin/svnlook log -r $REV $REPOS` | |
36 # AUTHOR=`/usr/bin/svnlook author -r $REV $REPOS` | |
37 # TRAC_ENV='/somewhere/trac/project/' | |
38 # TRAC_URL='http://trac.mysite.com/project/' | |
39 # | |
40 # /usr/bin/python /usr/local/src/trac/contrib/trac-post-commit-hook \ | |
41 # -p "$TRAC_ENV" \ | |
42 # -r "$REV" \ | |
43 # -u "$AUTHOR" \ | |
44 # -m "$LOG" \ | |
45 # -s "$TRAC_URL" | |
46 # | |
47 # It searches commit messages for text in the form of: | |
48 # command #1 | |
49 # command #1, #2 | |
50 # command #1 & #2 | |
51 # command #1 and #2 | |
52 # | |
53 # You can have more then one command in a message. The following commands | |
54 # are supported. There is more then one spelling for each command, to make | |
55 # this as user-friendly as possible. | |
56 # | |
57 # closes, fixes | |
58 # The specified issue numbers are closed with the contents of this | |
59 # commit message being added to it. | |
60 # references, refs, addresses, re | |
61 # The specified issue numbers are left in their current status, but | |
62 # the contents of this commit message are added to their notes. | |
63 # | |
64 # A fairly complicated example of what you can do is with a commit message | |
65 # of: | |
66 # | |
67 # Changed blah and foo to do this or that. Fixes #10 and #12, and refs #12. | |
68 # | |
69 # This will close #10 and #12, and add a note to #12. | |
70 | |
71 import re | |
72 import os | |
73 import sys | |
74 import time | |
75 | |
76 from trac.env import open_environment | |
77 from trac.ticket.notification import TicketNotifyEmail | |
78 from trac.ticket import Ticket | |
79 from trac.ticket.web_ui import TicketModule | |
80 # TODO: move grouped_changelog_entries to model.py | |
81 from trac.web.href import Href | |
82 | |
83 try: | |
84 from optparse import OptionParser | |
85 except ImportError: | |
86 try: | |
87 from optik import OptionParser | |
88 except ImportError: | |
89 raise ImportError, 'Requires Python 2.3 or the Optik option parsing library.' | |
90 | |
91 parser = OptionParser() | |
92 parser.add_option('-e', '--require-envelope', dest='env', default='', | |
93 help='Require commands to be enclosed in an envelope. If -e[], ' | |
94 'then commands must be in the form of [closes #4]. Must ' | |
95 'be two characters.') | |
96 parser.add_option('-p', '--project', dest='project', | |
97 help='Path to the Trac project.') | |
98 parser.add_option('-r', '--revision', dest='rev', | |
99 help='Repository revision number.') | |
100 parser.add_option('-u', '--user', dest='user', | |
101 help='The user who is responsible for this action') | |
102 parser.add_option('-m', '--msg', dest='msg', | |
103 help='The log message to search.') | |
104 parser.add_option('-s', '--siteurl', dest='url', | |
105 help='The base URL to the project\'s trac website (to which ' | |
106 '/ticket/## is appended). If this is not specified, ' | |
107 'the project URL from trac.ini will be used.') | |
108 | |
109 (options, args) = parser.parse_args(sys.argv[1:]) | |
110 | |
111 if options.env: | |
112 leftEnv = '\\' + options.env[0] | |
113 rghtEnv = '\\' + options.env[1] | |
114 else: | |
115 leftEnv = '' | |
116 rghtEnv = '' | |
117 | |
118 commandPattern = re.compile(leftEnv + r'(?P<action>[A-Za-z]*).?(?P<ticket>#[0-9]+(?:(?:[, &]*|[ ]?and[ ]?)#[0-9]+)*)' + rghtEnv) | |
119 ticketPattern = re.compile(r'#([0-9]*)') | |
120 | |
121 class CommitHook: | |
122 _supported_cmds = {'close': '_cmdClose', | |
123 'closed': '_cmdClose', | |
124 'closes': '_cmdClose', | |
125 'fix': '_cmdClose', | |
126 'fixed': '_cmdClose', | |
127 'fixes': '_cmdClose', | |
128 'addresses': '_cmdRefs', | |
129 're': '_cmdRefs', | |
130 'references': '_cmdRefs', | |
131 'refs': '_cmdRefs', | |
132 'see': '_cmdRefs'} | |
133 | |
134 def __init__(self, project=options.project, author=options.user, | |
135 rev=options.rev, msg=options.msg, url=options.url): | |
136 self.author = author | |
137 self.rev = rev | |
138 self.msg = "(In [%s]) %s" % (rev, msg) | |
139 self.now = int(time.time()) | |
140 self.env = open_environment(project) | |
141 if url is None: | |
142 url = self.env.config.get('project', 'url') | |
143 class Request(object): | |
144 def __init__(self): | |
145 self.href = self.abs_href = Href(url) | |
146 | |
147 cmdGroups = commandPattern.findall(msg) | |
148 | |
149 tickets = {} | |
150 for cmd, tkts in cmdGroups: | |
151 funcname = CommitHook._supported_cmds.get(cmd.lower(), '') | |
152 if funcname: | |
153 for tkt_id in ticketPattern.findall(tkts): | |
154 func = getattr(self, funcname) | |
155 tickets.setdefault(tkt_id, []).append(func) | |
156 | |
157 for tkt_id, cmds in tickets.iteritems(): | |
158 try: | |
159 db = self.env.get_db_cnx() | |
160 | |
161 ticket = Ticket(self.env, int(tkt_id), db) | |
162 for cmd in cmds: | |
163 cmd(ticket) | |
164 | |
165 # determine sequence number... | |
166 cnum = 0 | |
167 tm = TicketModule(self.env) | |
168 for change in tm.grouped_changelog_entries(ticket, db): | |
169 if change['permanent']: | |
170 cnum += 1 | |
171 | |
172 ticket.save_changes(self.author, self.msg, self.now, db, cnum+1) | |
173 db.commit() | |
174 | |
175 tn = TicketNotifyEmail(self.env) | |
176 tn.notify(ticket, Request(), newticket=0, modtime=self.now) | |
177 except Exception, e: | |
178 # import traceback | |
179 # traceback.print_exc(file=sys.stderr) | |
180 print>>sys.stderr, 'Unexpected error while processing ticket ' \ | |
181 'ID %s: %s' % (tkt_id, e) | |
182 | |
183 | |
184 def _cmdClose(self, ticket): | |
185 ticket['status'] = 'closed' | |
186 ticket['resolution'] = 'fixed' | |
187 | |
188 def _cmdRefs(self, ticket): | |
189 pass | |
190 | |
191 | |
192 if __name__ == "__main__": | |
193 if len(sys.argv) < 5: | |
194 print "For usage: %s --help" % (sys.argv[0]) | |
195 else: | |
196 CommitHook() |