hg_hooks/bugzilla/versiontobugzilla.py
author tahirm@symbian.org
Thu, 23 Jul 2009 12:50:28 +0100
changeset 22 f55ca49f7f44
child 42 ac3a70c9f81c
permissions -rw-r--r--
Added initial versions of filecheck and hg tags to bugzilla version hooks

'''Bugzilla integration for adding versions from tags

The hook updates the Bugzilla database directly. Only Bugzilla installations
using MySQL are supported.

This hook uses the same .hgrc parameters as the default Bugzilla hook. There
is no need for configuring the same stuff twice. (connection, etc.)

Configuring the extension: (same as Bugzilla -hook)

    [bugzilla]
    host       Hostname of the MySQL server holding the Bugzilla database.
    db         Name of the Bugzilla database in MySQL. Default 'bugs'.
    user       Username to use to access MySQL server. Default 'bugs'.
    password   Password to use to access MySQL server.
    timeout    Database connection timeout (seconds). Default 5.

Additional elements under Bugzilla -section: (new items)
    [bugzilla]
    product    The name on the Bugzilla product that is used for adding 
               the new versions.

Activating the extension:

    [extensions]
    hgext.versiontobugzilla =

    [hooks]
    incoming.versiontobugzilla = python:hgext.versiontobugzilla.hook

Example configuration in hgrc:
    [bugzilla]
    host = localhost
    user = bugs
    password = password
    product = my_product

    [extensions]
    hgext.versiontobugzilla =

    [hooks]
    incoming.versiontobugzilla = python:hgext.versiontobugzilla.hook
'''

from mercurial import util
import re

MySQLdb = None

class BugzillaClient:
    
    def __init__(self, ui, repo, node):
        self.tag = None
        self.ui = ui
        self.repo = repo
        self.node = node
        self.product = ui.config('bugzilla', 'product')
        self.host = ui.config('bugzilla', 'host', 'localhost')
        self.user = ui.config('bugzilla', 'user', 'bugs')
        self.passwd = ui.config('bugzilla', 'password')
        self.db = ui.config('bugzilla', 'db', 'bugs')
        self.timeout = int(ui.config('bugzilla', 'timeout', 10))
        self.connection = MySQLdb.connect(host=self.host, user=self.user, passwd=self.passwd,
                                    db=self.db, connect_timeout=self.timeout)
        self.cursor = self.connection.cursor()

    def printMessageInVerboseMode(self, message):
        '''Prints a message to console if hg has been executed with -v option.'''
        self.ui.note(message)

    def executeDatabaseQuery(self, *args, **kwargs):
        self.printMessageInVerboseMode('Bugzilla: query: %s %s\n' % (args, kwargs))
        try:
            self.cursor.execute(*args, **kwargs)
        except MySQLdb.MySQLError:
            self.printMessageInVerboseMode('Bugzilla: failed query: %s %s\n' % (args, kwargs))
            raise

    def commitContainsTag(self):
        self.parseTagFromCommitMessage()
        if self.tag:
            return True
        else:
            return False

    def parseTagFromCommitMessage(self):
        ctx = self.repo[self.node]
        version_re = re.compile(('Added tag (.+) for changeset [0-9a-h]+'), re.IGNORECASE)
        m = version_re.search(ctx.description())
        if m:
            self.tag = m.group(1)

    def insertTagIntoDatabase(self):
        self.makeSureThatProductExists()
        if not self.doesVersionAlreadyExist():
            self.printMessageInVerboseMode("Bugzilla: adding version '%s' to product '%s' in database.\n" % (self.tag, self.product))
            self.insertNewVersionIntoDatabase()
        else:
            self.printMessageInVerboseMode("Bugzilla: product '%s' already has a version '%s' in database. Not trying to add it again." % (self.product, self.tag))

    def makeSureThatProductExists(self):
        self.executeDatabaseQuery('select id from products where name = %s', (self.product,))
        ids = self.cursor.fetchall()
        if len(ids) != 1:
            raise util.Abort("Product '%s' does not exist in database, please check the [bugzilla] -section in hgrc." % self.product)

    def doesVersionAlreadyExist(self):
        self.executeDatabaseQuery('select * from versions where value = %s and product_id in (select id from products where name=%s )', (self.tag, self.product))
        ids = self.cursor.fetchall()
        if len(ids) == 1:
            return True
        else:
            return False

    def insertNewVersionIntoDatabase(self):
        self.executeDatabaseQuery('insert into versions (value, product_id) values (%s, (select id from products where name=%s ))', (self.tag, self.product))
        self.connection.commit()

def hook(ui, repo, hooktype, node=None, **kwargs):

    try:
        import MySQLdb as mysql
        global MySQLdb
        MySQLdb = mysql
    except ImportError, err:
        raise util.Abort('MySQL driver not installed: %s' % err)

    if node is None:
        raise util.Abort('Only hooks that have changesetid''s can be used.')

    try: 
        bzClient = BugzillaClient(ui, repo, node)
        if bzClient.commitContainsTag():
            bzClient.insertTagIntoDatabase()
    except MySQLdb.MySQLError, err:
        raise util.Abort('Database error: %s' % err[1])