source: repositoryhooksystemplugin/0.11/repository_hook_system/svnhooksystem.py

Last change on this file was 7099, checked in by Jeff Hammel, 14 years ago

use a blank for the default as ListOption will turn this into an empty list

File size: 4.8 KB
RevLine 
[4247]1"""
2implementation of the RepositoryChangeListener interface for svn
3"""
4
5import os
[5078]6import subprocess
[4247]7
[5079]8from dateutil.parser import parse
9
[4247]10from genshi.builder import tag
11from repository_hook_system.filesystemhooks import FileSystemHooks
12from repository_hook_system.interface import IRepositoryChangeListener
13from repository_hook_system.interface import IRepositoryHookSubscriber
14from repository_hook_system.interface import IRepositoryHookSystem
[5078]15from trac.config import Option
[4247]16from trac.config import ListOption
17from trac.core import *
18from trac.util.text import CRLF
[5317]19from trac.versioncontrol import NoSuchChangeset
[6999]20from trac.web.chrome import add_warning
[4247]21from utils import iswritable
22
23class SVNHookSystem(FileSystemHooks):
24    """implementation of IRepositoryChangeListener for SVN repositories"""
25
26    implements(IRepositoryHookSystem, IRepositoryChangeListener)
27    listeners = ExtensionPoint(IRepositoryHookSubscriber)
28    hooks = [ 'pre-commit', 'post-commit', 'pre-revprop-change', 'post-revprop-change' ]
29
[5078]30
31    _svnlook = Option('svn', 'svnlook', default='/usr/bin/svnlook')
32
[4247]33    ### methods for FileSystemHooks
34
35    def filename(self, hookname):
36        location = self.env.config.get('trac', 'repository_dir')
37        return os.path.join(location, 'hooks', hookname)
38
39    def args(self):
40        return [ '$2' ]
41
42    ### methods for IRepositoryHookAdminContributer
43
44    def render(self, hookname, req):
45        filename = self.filename(hookname)
46        try:
47            contents = file(filename).read() # check for CRLF here too?
[5056]48            return tag.textarea(contents, rows='25', cols='80', name='hook-file-contents', disabled=not self.can_enable(hookname) or None)
49
[4247]50        except IOError:
[5056]51            if self.can_enable(filename):
[4247]52                text = "No %s hook file yet exists;  enable this hook to create one" % hookname
53            else:
[5056]54                text = "The file, %s, is unwritable;  enabling this hook will have no effect" % filename
[4247]55            return text
56
57    def process_post(self, hookname, req):
58       
59        contents = req.args.get('hook-file-contents', None)
60        if contents is None:
61            return
62        if os.linesep != CRLF:
63            contents = os.linesep.join(contents.split(CRLF)) # form contents will have this
64
65        filename = self.filename(hookname)
66        if not os.path.exists(filename):
67            if not iswritable(filename):
[6999]68                add_warning(req, 'File "%s" not writable' % filename)
69                return
[4247]70        f = file(filename, 'w')
71        print >> f, contents
[6999]72        f.close()
73        try:
74            os.chmod(filename, self.mode)
75        except: # won't work on winblows
76            pass
[4247]77
78    ### methods for IRepositoryChangeListener
79
80    def type(self):
81        return ['svn', 'svnsync']
82
83    def available_hooks(self):
84        return self.hooks
85
86    def subscribers(self, hookname):
87        """returns the active subscribers for a given hook name"""
88       
89        # XXX this is all SCM-agnostic;  should be moved out
90        return [ subscriber for subscriber in self.listeners
91                 if subscriber.__class__.__name__ 
92                 in getattr(self, hookname, []) 
93                 and subscriber.is_available(self.type(), hookname) ]
94
[5078]95    def changeset(self, repo, hookname, commit_id):
[4247]96        """
97        return the changeset given the repository object and revision number
98        """
99
[5078]100        if hookname in ['post-commit', 'post-revprop-change']:
101            revision = commit_id
102            try:
103               
104                chgset = repo.get_changeset(revision)
105            except NoSuchChangeset:
106                # XXX should probably throw an exception (same one?)
[5317]107                raise # out of scope changesets are not cached
[5078]108            return chgset
109        else:
110            transaction = commit_id
111            repo = self.env.config.get('trac', 'repository_dir')
112
113            def svnlook(subcommand, *args):
114
[5079]115                process = subprocess.Popen([self._svnlook, subcommand, repo, '-t', transaction] + list(args), stdout=subprocess.PIPE)
[5078]116                return process.communicate()[0]
117
118            # get the attributes
119            author = svnlook('author').strip()
120            date = parse(svnlook('date').split('(')[0].strip())
[5317]121            # XXX FIXME
122#             from datetime import datetime
123#             date = datetime.now()
[5078]124
125           
126            message = svnlook('log').strip()
127            rev = transaction
128
129            attributes = dict(author=author,
130                              date=date,
131                              message=message,
132                              rev=rev)
133            chgset = type('DummyChangeset', (object,), attributes)
134            return chgset()
135           
136
[4247]137for hook in SVNHookSystem.hooks:
138    setattr(SVNHookSystem, hook, 
[7099]139            ListOption('repository-hooks', hook, default='',
[4247]140                       doc="active listeners for SVN changes on the %s hook" % hook))
Note: See TracBrowser for help on using the repository browser.