|
1 '''Bugzilla integration for adding versions from tags |
|
2 |
|
3 The hook updates the Bugzilla database directly. Only Bugzilla installations |
|
4 using MySQL are supported. |
|
5 |
|
6 This hook uses the same .hgrc parameters as the default Bugzilla hook. There |
|
7 is no need for configuring the same stuff twice. (connection, etc.) |
|
8 |
|
9 Configuring the extension: (same as Bugzilla -hook) |
|
10 |
|
11 [bugzilla] |
|
12 host Hostname of the MySQL server holding the Bugzilla database. |
|
13 db Name of the Bugzilla database in MySQL. Default 'bugs'. |
|
14 user Username to use to access MySQL server. Default 'bugs'. |
|
15 password Password to use to access MySQL server. |
|
16 timeout Database connection timeout (seconds). Default 5. |
|
17 |
|
18 Additional elements under Bugzilla -section: (new items) |
|
19 [bugzilla] |
|
20 product The name on the Bugzilla product that is used for adding |
|
21 the new versions. |
|
22 |
|
23 Activating the extension: |
|
24 |
|
25 [extensions] |
|
26 hgext.versiontobugzilla = |
|
27 |
|
28 [hooks] |
|
29 incoming.versiontobugzilla = python:hgext.versiontobugzilla.hook |
|
30 |
|
31 Example configuration in hgrc: |
|
32 [bugzilla] |
|
33 host = localhost |
|
34 user = bugs |
|
35 password = password |
|
36 product = my_product |
|
37 |
|
38 [extensions] |
|
39 hgext.versiontobugzilla = |
|
40 |
|
41 [hooks] |
|
42 incoming.versiontobugzilla = python:hgext.versiontobugzilla.hook |
|
43 ''' |
|
44 |
|
45 from mercurial import util |
|
46 import re |
|
47 |
|
48 MySQLdb = None |
|
49 |
|
50 class BugzillaClient: |
|
51 |
|
52 def __init__(self, ui, repo, node): |
|
53 self.tag = None |
|
54 self.ui = ui |
|
55 self.repo = repo |
|
56 self.node = node |
|
57 self.product = ui.config('bugzilla', 'product') |
|
58 self.host = ui.config('bugzilla', 'host', 'localhost') |
|
59 self.user = ui.config('bugzilla', 'user', 'bugs') |
|
60 self.passwd = ui.config('bugzilla', 'password') |
|
61 self.db = ui.config('bugzilla', 'db', 'bugs') |
|
62 self.timeout = int(ui.config('bugzilla', 'timeout', 10)) |
|
63 self.connection = MySQLdb.connect(host=self.host, user=self.user, passwd=self.passwd, |
|
64 db=self.db, connect_timeout=self.timeout) |
|
65 self.cursor = self.connection.cursor() |
|
66 |
|
67 def printMessageInVerboseMode(self, message): |
|
68 '''Prints a message to console if hg has been executed with -v option.''' |
|
69 self.ui.note(message) |
|
70 |
|
71 def executeDatabaseQuery(self, *args, **kwargs): |
|
72 self.printMessageInVerboseMode('Bugzilla: query: %s %s\n' % (args, kwargs)) |
|
73 try: |
|
74 self.cursor.execute(*args, **kwargs) |
|
75 except MySQLdb.MySQLError: |
|
76 self.printMessageInVerboseMode('Bugzilla: failed query: %s %s\n' % (args, kwargs)) |
|
77 raise |
|
78 |
|
79 def commitContainsTag(self): |
|
80 self.parseTagFromCommitMessage() |
|
81 if self.tag: |
|
82 return True |
|
83 else: |
|
84 return False |
|
85 |
|
86 def parseTagFromCommitMessage(self): |
|
87 ctx = self.repo[self.node] |
|
88 version_re = re.compile(('Added tag (.+) for changeset [0-9a-h]+'), re.IGNORECASE) |
|
89 m = version_re.search(ctx.description()) |
|
90 if m: |
|
91 self.tag = m.group(1) |
|
92 |
|
93 def insertTagIntoDatabase(self): |
|
94 self.makeSureThatProductExists() |
|
95 if not self.doesVersionAlreadyExist(): |
|
96 self.printMessageInVerboseMode("Bugzilla: adding version '%s' to product '%s' in database.\n" % (self.tag, self.product)) |
|
97 self.insertNewVersionIntoDatabase() |
|
98 else: |
|
99 self.printMessageInVerboseMode("Bugzilla: product '%s' already has a version '%s' in database. Not trying to add it again." % (self.product, self.tag)) |
|
100 |
|
101 def makeSureThatProductExists(self): |
|
102 self.executeDatabaseQuery('select id from products where name = %s', (self.product,)) |
|
103 ids = self.cursor.fetchall() |
|
104 if len(ids) != 1: |
|
105 raise util.Abort("Product '%s' does not exist in database, please check the [bugzilla] -section in hgrc." % self.product) |
|
106 |
|
107 def doesVersionAlreadyExist(self): |
|
108 self.executeDatabaseQuery('select * from versions where value = %s and product_id in (select id from products where name=%s )', (self.tag, self.product)) |
|
109 ids = self.cursor.fetchall() |
|
110 if len(ids) == 1: |
|
111 return True |
|
112 else: |
|
113 return False |
|
114 |
|
115 def insertNewVersionIntoDatabase(self): |
|
116 self.executeDatabaseQuery('insert into versions (value, product_id) values (%s, (select id from products where name=%s ))', (self.tag, self.product)) |
|
117 self.connection.commit() |
|
118 |
|
119 def hook(ui, repo, hooktype, node=None, **kwargs): |
|
120 |
|
121 try: |
|
122 import MySQLdb as mysql |
|
123 global MySQLdb |
|
124 MySQLdb = mysql |
|
125 except ImportError, err: |
|
126 raise util.Abort('MySQL driver not installed: %s' % err) |
|
127 |
|
128 if node is None: |
|
129 raise util.Abort('Only hooks that have changesetid''s can be used.') |
|
130 |
|
131 try: |
|
132 bzClient = BugzillaClient(ui, repo, node) |
|
133 if bzClient.commitContainsTag(): |
|
134 bzClient.insertTagIntoDatabase() |
|
135 except MySQLdb.MySQLError, err: |
|
136 raise util.Abort('Database error: %s' % err[1]) |