source: configuration/trac/trac-post-commit-hook @ 1302:6cd648f81ec4

Revision 1302:6cd648f81ec4, 7.9 KB checked in by niam, 3 years ago (diff)

configuration: added post-commit hook for trac

Line 
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# TRAC_ENV="/path/to/tracenv"
36#
37# /usr/bin/python /usr/local/src/trac/contrib/trac-post-commit-hook \
38#  -p "$TRAC_ENV" -r "$REV"
39#
40# It searches commit messages for text in the form of:
41#   (issue #1[command]}
42#   (issue #1, #2[command]}
43#   (issue #1 and #2[command]}
44#
45# In addition, the ':' character can be omitted and issue or bug can be used
46# instead of ticket.
47#
48# You can have more than one command in a message. The following commands
49# are supported. There is more than one spelling for each command, to make
50# this as user-friendly as possible.
51#
52#   close, closed, closes, fix, fixed, fixes, resolve, resolved, resolves
53#     The specified issue numbers are closed with the contents of this
54#     commit message being added to it.
55#   references, refs, addresses, re, see
56#     The specified issue numbers are left in their current status, but
57#     the contents of this commit message are added to their notes.
58#
59# A fairly complicated example of what you can do is with a commit message
60# of:
61#
62#    Changed blah and foo to do this or that. Fixes #10 and #12, and refs #12.
63#
64# This will close #10 and #12, and add a note to #12.
65
66import re
67import os
68import sys
69from datetime import datetime
70from optparse import OptionParser
71
72parser = OptionParser()
73depr = '(not used anymore)'
74parser.add_option('-e', '--require-envelope', dest='envelope', default='',
75                  help="""
76Require commands to be enclosed in an envelope.
77If -e[], then commands must be in the form of [closes #4].
78Must be two characters.""")
79parser.add_option('-p', '--project', dest='project',
80                  help='Path to the Trac project.')
81parser.add_option('-r', '--revision', dest='rev',
82                  help='Repository revision number.')
83parser.add_option('-u', '--user', dest='user',
84                  help='The user who is responsible for this action '+depr)
85parser.add_option('-m', '--msg', dest='msg',
86                  help='The log message to search '+depr)
87parser.add_option('-c', '--encoding', dest='encoding',
88                  help='The encoding used by the log message '+depr)
89parser.add_option('-s', '--siteurl', dest='url',
90                  help=depr+' the base_url from trac.ini will always be used.')
91
92(options, args) = parser.parse_args(sys.argv[1:])
93
94if not 'PYTHON_EGG_CACHE' in os.environ:
95    os.environ['PYTHON_EGG_CACHE'] = os.path.join(options.project, '.egg-cache')
96
97from trac.env import open_environment
98from trac.ticket.notification import TicketNotifyEmail
99from trac.ticket import Ticket
100from trac.ticket.web_ui import TicketModule
101# TODO: move grouped_changelog_entries to model.py
102from trac.util.text import to_unicode
103from trac.util.datefmt import utc
104from trac.versioncontrol.api import NoSuchChangeset
105
106ticket_prefix = '(?:#|(?:ticket|issue|bug) *[#:]?)'
107ticket_reference = ticket_prefix + '[0-9]+'
108ticket_command =  (r'(?P<ticket>%s(?:(?:[, ]*|[ ]?and[ ]?)%s)*)'
109                   '\[(?P<action>[A-Za-z]*)\].?' %
110                   (ticket_reference, ticket_reference))
111
112if options.envelope:
113    ticket_command = r'\%s%s\%s' % (options.envelope[0], ticket_command,
114                                    options.envelope[1])
115   
116command_re = re.compile(ticket_command)
117ticket_re = re.compile(ticket_prefix + '([0-9]+)')
118
119class CommitHook:
120    _supported_cmds = {'close':      '_cmdClose',
121                       'closed':     '_cmdClose',
122                       'closes':     '_cmdClose',
123                       'fix':        '_cmdClose',
124                       'fixed':      '_cmdClose',
125                       'fixes':      '_cmdClose',
126                       'resolve':    '_cmdClose',
127                       'resolved':   '_cmdClose',
128                       'resolves':   '_cmdClose',
129                       'addresses':  '_cmdRefs',
130                       're':         '_cmdRefs',
131                       'references': '_cmdRefs',
132                       'refs':       '_cmdRefs',
133                       'see':        '_cmdRefs'}
134
135    def __init__(self, project=options.project, author=options.user,
136                 rev=options.rev, url=options.url):
137        self.env = open_environment(project)
138        repos = self.env.get_repository()
139        repos.sync()
140       
141        # Instead of bothering with the encoding, we'll use unicode data
142        # as provided by the Trac versioncontrol API (#1310).
143        try:
144            chgset = repos.get_changeset(rev)
145        except NoSuchChangeset:
146            return # out of scope changesets are not cached
147        self.author = chgset.author
148        self.rev = rev
149        self.msg = "(In [%s]) %s" % (rev, chgset.message)
150        self.now = datetime.now(utc)
151
152        cmd_groups = command_re.findall(self.msg)
153
154        tickets = {}
155        for tkts, cmd in cmd_groups:
156            funcname = CommitHook._supported_cmds.get(cmd.lower(), '')
157            if funcname:
158                for tkt_id in ticket_re.findall(tkts):
159                    func = getattr(self, funcname)
160                    tickets.setdefault(tkt_id, []).append(func)
161
162        for tkt_id, cmds in tickets.iteritems():
163            try:
164                db = self.env.get_db_cnx()
165               
166                ticket = Ticket(self.env, int(tkt_id), db)
167                for cmd in cmds:
168                    cmd(ticket)
169
170                # determine sequence number...
171                cnum = 0
172                tm = TicketModule(self.env)
173                for change in tm.grouped_changelog_entries(ticket, db):
174                    if change['permanent']:
175                        cnum += 1
176               
177                ticket.save_changes(self.author, self.msg, self.now, db, cnum+1)
178                db.commit()
179               
180                tn = TicketNotifyEmail(self.env)
181                tn.notify(ticket, newticket=0, modtime=self.now)
182            except Exception, e:
183                # import traceback
184                # traceback.print_exc(file=sys.stderr)
185                print>>sys.stderr, 'Unexpected error while processing ticket ' \
186                                   'ID %s: %s' % (tkt_id, e)
187           
188
189    def _cmdClose(self, ticket):
190        ticket['status'] = 'closed'
191        ticket['resolution'] = 'fixed'
192
193    def _cmdRefs(self, ticket):
194        pass
195
196
197if __name__ == "__main__":
198    if len(sys.argv) < 5:
199        print "For usage: %s --help" % (sys.argv[0])
200        print
201        print "Note that the deprecated options will be removed in Trac 0.12."
202    else:
203        CommitHook()
Note: See TracBrowser for help on using the repository browser.