SVN/Trac Integration

A long time ago I wrote an article titled CVS/Bugzilla Integration. Here is an updated version for subversion and trac.

Note that it doesn’t require access the trac python libs or database; it just uses HTTP. But it does require BeautifulSoup. Install it by adding it to your hooks post-commit script. (it takes the normal params of REPOS and REV).

#!/usr/bin/env python

import os, re, sys, urllib, urllib2

from BeautifulSoup import BeautifulSoup

TRAC_URL = 'http://my.trac.url'
TRAC_TICKET_REL_URL = '/xxx/ticket/'
TRAC_LOGIN_REL_URL = '/xxx/login/'
TRAC_USER = 'user'
TRAC_PASS = 'pass'
TRAC_REALM = 'myRealm'
#SVNLOOK = '/usr/bin/svnlook'
SVNLOOK = '/usr/local/bin/svnlook'

TRAC_TICKET_URL = TRAC_URL+TRAC_TICKET_REL_URL
TRAC_LOGIN_URL = TRAC_URL+TRAC_LOGIN_REL_URL

def get_commit_info(repos, rev):
    c = os.popen( SVNLOOK+' info "%s" -r "%s"' % (repos, rev) )
    user = c.readline().strip()
    date = c.readline().strip()
    ignore = c.readline().strip()
    msg = c.read()
    c.close()
    return user, date, msg

def get_trac_tickets(msg):
    p = re.compile(r'trac:\d+')
    tickets = p.findall(msg)
    return [ t[5:] for t in tickets ]

def post_to_trac( ticket, msg ):
    auth = urllib2.HTTPDigestAuthHandler()
    auth.add_password(TRAC_REALM, TRAC_URL, TRAC_USER, TRAC_PASS)
    opener = urllib2.build_opener(auth, urllib2.HTTPCookieProcessor())
    req = urllib2.Request(TRAC_LOGIN_URL)
    req.add_header("Referer", TRAC_TICKET_URL+ticket)
    try:
        res = opener.open(req)
        html = res.read()
        soup = BeautifulSoup(html)
        form = soup.find('form', action=TRAC_TICKET_REL_URL+ticket+'#preview')
        data = {}
        for i in form.findAll('input'):
            try:
                if i['type']=='hidden' or i['type']=='text':
                    data[ i['name'] ] = i['value']
                if i['type']=='checkbox' or i['type']=='radio':
                    if i.get('checked'):
                        data[ i['name'] ] = i['value']
            except: pass
        for s in form.findAll('select'):
            for o in s.findAll('option'):
                if o.get('selected'):
                    data[ s['name'] ] = o.get('value',o.string)
        data['comment'] = msg
        #data['preview'] = 'Preview'
        #print '\n'.join([ str(x) for x in data.iteritems() ] )
        req = urllib2.Request(TRAC_TICKET_URL+ticket, urllib.urlencode(data))
        req.add_header("Referer", TRAC_TICKET_URL+ticket)
        res = opener.open(req)
        html = res.read()
        #print html

    except IOError, e:
        pass
#      if hasattr(e, 'code'):
#        if e.code != 401:
#            print 'We got another error'
#            print e.code
#        else:
#            print e.headers

def main(*args):

    if len(args)!=3:
        print 'usage: python commit-trac.py "repos" "rev"'
        return 1

    repos = args[1]
    rev = args[2]
    user, date, msg = get_commit_info(repos, rev)
    msg = 'svn commit to %s -r%s (%s):\n%s' % (repos, rev, user, msg)
    print msg

    for ticket in get_trac_tickets(msg):
        post_to_trac( ticket, msg )

    return 0 # exit errorlessly

if __name__ == '__main__':
    sys.exit(main(*sys.argv))

Leave a Reply


<Kered.org>   © Copyright 2000-2005 by Derek Anderson
Get Firefox