trac-repositoryhooksystem / 0.11 / repository_hook_system /

implementation of the RepositoryChangeListener interface for svn

import os
import subprocess

from genshi.builder import tag
from repository_hook_system.filesystemhooks import FileSystemHooks
from repository_hook_system.interface import IRepositoryChangeListener
from repository_hook_system.interface import IRepositoryHookSubscriber
from repository_hook_system.interface import IRepositoryHookSystem
from trac.config import Option
from trac.config import ListOption
from trac.core import *
from trac.util.text import CRLF
from utils import iswritable

class SVNHookSystem(FileSystemHooks):
    """implementation of IRepositoryChangeListener for SVN repositories"""

    implements(IRepositoryHookSystem, IRepositoryChangeListener)
    listeners = ExtensionPoint(IRepositoryHookSubscriber)
    hooks = [ 'pre-commit', 'post-commit', 'pre-revprop-change', 'post-revprop-change' ]

    _svnlook = Option('svn', 'svnlook', default='/usr/bin/svnlook')

    ### methods for FileSystemHooks

    def filename(self, hookname):
        location = self.env.config.get('trac', 'repository_dir')
        return os.path.join(location, 'hooks', hookname)

    def args(self):
        return [ '$2' ]

    ### methods for IRepositoryHookAdminContributer

    def render(self, hookname, req):
        filename = self.filename(hookname)
            contents = file(filename).read() # check for CRLF here too?
            return tag.textarea(contents, rows='25', cols='80', name='hook-file-contents', disabled=not self.can_enable(hookname) or None)

        except IOError:
            if self.can_enable(filename):
                text = "No %s hook file yet exists;  enable this hook to create one" % hookname
                text = "The file, %s, is unwritable;  enabling this hook will have no effect" % filename
            return text

    def process_post(self, hookname, req):
        contents = req.args.get('hook-file-contents', None)
        if contents is None:
        if os.linesep != CRLF:
            contents = os.linesep.join(contents.split(CRLF)) # form contents will have this

        filename = self.filename(hookname)
        if not os.path.exists(filename):
            if not iswritable(filename):
                return # XXX error handling?
        f = file(filename, 'w')
        print >> f, contents

    ### methods for IRepositoryChangeListener

    def type(self):
        return ['svn', 'svnsync']

    def available_hooks(self):
        return self.hooks

    def subscribers(self, hookname):
        """returns the active subscribers for a given hook name"""
        # XXX this is all SCM-agnostic;  should be moved out
        return [ subscriber for subscriber in self.listeners 
                 if subscriber.__class__.__name__ 
                 in getattr(self, hookname, []) 
                 and subscriber.is_available(self.type(), hookname) ]

    def changeset(self, repo, hookname, commit_id):
        return the changeset given the repository object and revision number

        if hookname in ['post-commit', 'post-revprop-change']:
            revision = commit_id
                chgset = repo.get_changeset(revision)
            except NoSuchChangeset:
                # XXX should probably throw an exception (same one?)
                return # out of scope changesets are not cached
            return chgset
            transaction = commit_id
            repo = self.env.config.get('trac', 'repository_dir')

            def svnlook(subcommand, *args):

                process = subprocess.Popen([self._svnlook, subcommand, repo, '-r', transaction] + list(args), stdout=subprocess.PIPE)
                return process.communicate()[0]

            # get the attributes
            author = svnlook('author').strip()
            from dateutil.parser import parse
#            import pdb;  pdb.set_trace()
            date = parse(svnlook('date').split('(')[0].strip())

            message = svnlook('log').strip()
            rev = transaction

            # XXX FIXME
#             from datetime import datetime
#             date =

            attributes = dict(author=author,
            chgset = type('DummyChangeset', (object,), attributes)
            return chgset()

for hook in SVNHookSystem.hooks:
    setattr(SVNHookSystem, hook, 
            ListOption('repository-hooks', hook, default=[],
                       doc="active listeners for SVN changes on the %s hook" % hook))