Changeset 3581

Show
Ignore:
Timestamp:
04/30/08 17:41:53 (5 months ago)
Author:
eblot
Message:

Update hook scripts for the RevtreePlugin/LogEnhancer

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • revtreeplugin/0.11/hooks/README

    • Property svn:eol-style set to native
  • revtreeplugin/0.11/hooks/repproxy.py

    • Property svn:eol-style set to native
    r2638 r3581  
    11#!/usr/bin/env python 
     2# -*- coding: utf8 -*- 
    23# 
    3 # repproxy.py 
    4 # ---------------------------------------------------------------------------- 
    5 # Copyright (c) 2005-2007 Emmanuel Blot 
    6 # ---------------------------------------------------------------------------- 
     4# Copyright (c) 2005-2008 Emmanuel Blot 
     5
    76 
    87import sys 
     
    7877        for chgpath in chgpaths: 
    7978            (srcrev, srcpath) = fs.svn_fs_copied_from(root, chgpath, self.pool) 
     79            #print >>sys.stderr, "chgpath: %s -> %s @ %d" % (chgpath, srcpath, srcrev) 
    8080            if srcrev > 0 and srcpath is not None: 
    8181                return (srcrev, srcpath) 
     
    9393        for chgpath in chgpaths: 
    9494            (srcrev, srcpath) = fs.svn_fs_copied_from(root, chgpath, self.pool) 
     95            #print >>sys.stderr, "chgpath: %s -> %s @ %d" % (chgpath, srcpath, srcrev) 
    9596            if srcrev > 0 and srcpath is not None: 
    9697                return (srcrev, srcpath) 
     
    135136    def get_revision_changed_paths(self, revision): 
    136137        root = self.get_revision_root(revision) 
    137         for (path, change) in self._get_changed_paths(root): 
    138             yield path 
     138        return self._get_changed_paths(root) 
    139139 
    140140    def _get_changed_paths(self, root): 
     
    152152        return rev 
    153153 
    154     def get_history(self, revision, path, traverse=1): 
     154    def get_history(self, revision, path, traverse=True): 
    155155        """Provides a generator to iterate through the history of a path 
    156156           
     
    164164  
    165165        # svn_repos_history does not support the None argument 
    166         if not traverse: 
    167             traverse = 0 
    168166        repos.svn_repos_history(self.fs, path, history_cb, 0, revision, \ 
    169                                 traverse, self.pool) 
     167                                traverse and 1 or 0, self.pool) 
    170168        for h in history: 
    171169            yield h 
     
    177175        for rev in range(youngest,0,-1): 
    178176            root = self.get_revision_root(rev) 
    179             for revpath in self.get_revision_changed_paths(rev): 
     177            for (revpath, change) in self.get_revision_changed_paths(rev): 
    180178                if revpath[:length] == path: 
    181179                    return rev 
  • revtreeplugin/0.11/hooks/svnstubs/post-commit

    • Property svn:eol-style set to native
    r2638 r3581  
    11#!/bin/sh 
    2  
    3 # POST-COMMIT HOOK 
    4 # 
    5 #   [1] REPOS-PATH   (the path to this repository) 
    6 #   [2] TRANSACTION  (the transaction being processed) 
    72 
    83REPOS="$1" 
    94REV="$2" 
    10 CONFIG_PATH=`dirname $0` 
    115 
    12 # Load global config 
    13 if [ -f "${CONFIG_PATH}/config" ]; then 
    14   . ${CONFIG_PATH}/config 
     6SELF=`readlink -f $0` 
     7CONF=`dirname ${SELF}`/hook.conf 
     8if [ -f ${CONF} ]; then 
     9    . ${CONF} 
    1510else 
    16   echo "Missing config file" >&2 
     11    echo "Missing hook configuration file" >&2 
     12    exit -1 
    1713fi 
    1814 
    19 REPOSNAME=`echo "$REPOS" | sed -r 's/^.*\/([^\/]+)$/\1/'` 
    20 TRAC_ENV="${TRAC_ENV_PARENT_DIR}/${REPOSNAME}" 
    21 LOGFILE="${TRAC_ENV}/log/svn-postcommit.log" 
    22  
    23 export PYTHONPATH=$TRACPATH 
     15export PYTHONPATH=$TRACPATH:$GENSHIPATH 
     16export PYTHON_EGG_CACHE 
     17export PYTHON=`which python2.5` 
    2418date >> "$LOGFILE" 
    2519echo "Revision: $REV" >> "$LOGFILE" 
    26 ${PYTHON} ${TRAC_PATH}/hooks/trac-commit-hook
    27     -d "$REPOS" -r "$REV" -p "$TRAC_ENV" 2>> "$LOGFILE" 
     20${PYTHON} ${HOOKPATH}/trac-commit-hook -d "$REPOS" -r "$REV" -p "$TRAC_ENV"
     21    2>> "$LOGFILE" 
    2822if [ $? -ne 0 ]; then 
    29   if [ -n ${MAIL_ADMIN} ]; 
    30     tail -5 "$LOGFILE" | mail ${MAIL_ADMIN} \ 
    31       -s "[${REPOSNAME}] Unable to post-commit revision ${REV}" 
     23    tail -5 "$LOGFILE" | \ 
     24      cat 
     25      #mail admin -s "[${REPOSNAME}] Unable to post-commit revision ${REV}" 
    3226fi 
     27 
  • revtreeplugin/0.11/hooks/svnstubs/post-revprop-change

    • Property svn:eol-style set to native
    r2638 r3581  
    1717PROPNAME="$4" 
    1818ACTION="$5" 
    19 CONFIG_PATH=`dirname $0` 
    2019 
    21 if [ -f "${CONFIG_PATH}/config" ]; then 
    22   . ${CONFIG_PATH}/config 
     20SELF=`readlink -f $0` 
     21CONF=`dirname ${SELF}`/hook.conf 
     22if [ -f ${CONF} ]; then 
     23    . ${CONF} 
    2324else 
    24   echo "Missing config file" >&2 
     25    echo "Missing hook configuration file" >&2 
     26    exit -1 
    2527fi 
    2628 
    27 REPOSNAME=`echo "$REPOS" | sed -r 's/^.*\/([^\/]+)$/\1/'` 
    28 TRAC_ENV="${TRAC_ENV_PARENT_DIR}/${REPOSNAME}" 
    29 LOGFILE="${TRAC_ENV}/log/svn-postrevpropchange.log" 
    30  
    31 export PYTHONPATH=$TRACPATH 
     29export PYTHONPATH=$TRACPATH:$GENSHIPATH 
     30export PYTHON_EGG_CACHE 
     31export PYTHON=`which python2.5` 
    3232date >> "$LOGFILE" 
    3333echo "Revprop: $REV $PROPNAME" >> $LOGFILE 
    34 ${PYTHON} ${TRACPATH}/hooks/trac-revprop-hook -p "$TRAC_ENV" -u "$USER" \ 
     34${PYTHON} ${HOOKPATH}/trac-revprop-hook -p "$TRAC_ENV" -u "$USER" \ 
    3535    -n "$PROPNAME" -d "$REPOS" -r "$REV" -a "$ACTION" post 2>> "$LOGFILE" 
    3636if [ $? -ne 0 ]; then 
  • revtreeplugin/0.11/hooks/svnstubs/pre-commit

    • Property svn:eol-style set to native
    r2638 r3581  
    11#!/bin/sh 
    2  
    3 # PRE-COMMIT HOOK 
    4 # 
    5 #   [1] REPOS-PATH   (the path to this repository) 
    6 #   [2] TRANSACTION  (the transaction being processed) 
    72 
    83REPOS="$1" 
    94TXN="$2" 
    10 CONFIG_PATH=`dirname $0` 
    115 
    12 if [ -f "${CONFIG_PATH}/config" ]; then 
    13   . ${CONFIG_PATH}/config 
     6SELF=`readlink -f $0` 
     7CONF=`dirname ${SELF}`/hook.conf 
     8if [ -f ${CONF} ]; then 
     9    . ${CONF} 
    1410else 
    15   echo "Missing config file" >&2 
     11    echo "Missing hook configuration file (${CONF})" >&2 
     12    exit -1 
    1613fi 
    1714 
    18 REPOSNAME=`echo "$REPOS" | sed -r 's/^.*\/([^\/]+)$/\1/'` 
    19 TRAC_ENV="${TRAC_ENV_PARENT_DIR}/${REPOSNAME}" 
    20 LOGFILE="${TRAC_ENV}/log/svn-precommit.log" 
    21  
    22 export PYTHONPATH=$TRACPATH 
     15export PYTHONPATH=$TRACPATH:$GENSHIPATH 
     16export PYTHON_EGG_CACHE 
     17export PYTHON=`which python2.5` 
    2318echo "Transaction: $TXN" >> $LOGFILE 
    24 ${PYTHON} ${TRACPATH}/hooks/trac-commit-hook \ 
    25     -d "$REPOS" -t "$TXN" -p "$TRAC_ENV"  
     19${PYTHON} ${HOOKPATH}/trac-commit-hook -d "$REPOS" -t "$TXN" -p "$TRAC_ENV"  
    2620RC=$? 
    2721echo "RESULT $RC" >>  $LOGFILE 
  • revtreeplugin/0.11/hooks/svnstubs/pre-revprop-change

    • Property svn:eol-style set to native
    r2638 r3581  
    1515PROPNAME="$4" 
    1616ACTION="$5" 
    17 CONFIG_PATH=`dirname $0` 
    1817 
    19 if [ -f "${CONFIG_PATH}/config" ]; then 
    20   . ${CONFIG_PATH}/config 
     18SELF=`readlink -f $0` 
     19CONF=`dirname ${SELF}`/hook.conf 
     20if [ -f ${CONF} ]; then 
     21    . ${CONF} 
    2122else 
    22   echo "Missing config file" >&2 
     23    echo "Missing hook configuration file" >&2 
     24    exit -1 
    2325fi 
    2426 
    25 REPOSNAME=`echo "$REPOS" | sed -r 's/^.*\/([^\/]+)$/\1/'` 
    26 TRAC_ENV="${TRAC_ENV_PARENT_DIR}/${REPOSNAME}" 
    27 LOGFILE="${TRAC_ENV}/log/svn-prerevpropchange.log" 
    28  
    29 export PYTHONPATH=$TRACPATH 
     27export PYTHONPATH=$TRACPATH:$GENSHIPATH 
     28export PYTHON_EGG_CACHE 
     29export PYTHON=`which python2.5` 
    3030echo "Revprop: $REV $PROPNAME action:$ACTION" >> $LOGFILE 
    31 ${PYTHON} ${TRACPATH}/hooks/trac-revprop-hook -p "$TRAC_ENV" -u "$USER" \ 
    32     -n "$PROPNAME" -d "$REPOS" -r "$REV" -a "$ACTION" pre  
     31${PYTHON} ${HOOKPATH}/trac-revprop-hook -p "$TRAC_ENV" -u "$USER" \ 
     32    -n "$PROPNAME" -d "$REPOS" -r "$REV" -a "$ACTION" pre 2>> "$LOGFILE" 
    3333RC=$? 
    3434echo "RESULT $RC" >>  $LOGFILE 
  • revtreeplugin/0.11/hooks/svnstubs/README

    • Property svn:eol-style set to native
    r2638 r3581  
    33environment. 
    44 
    5 Do not forget to edit the 'config' file to match your environment. 
     5Do not forget to edit the 'hook.conf' file to match your environment. 
  • revtreeplugin/0.11/hooks/trac-commit-hook

    • Property svn:eol-style set to native
    r2638 r3581  
    44# ---------------------------------------------------------------------------- 
    55# Copyright (c) 2004 Stephen Hansen  
    6 # Copyright (c) 2005-2007 Emmanuel Blot, Jerome Souquieres 
     6# Copyright (c) 2005-2008 Emmanuel Blot, Jerome Souquieres 
    77# 
    88# Permission is hereby granted, free of charge, to any person obtaining a copy 
     
    1616#   all copies or substantial portions of the Software.  
    1717# ---------------------------------------------------------------------------- 
    18  
    19 # This Subversion unified pre/post-commit hook script is meant to interface 
    20 # to the Trac (http://www.edgewall.com/products/trac/) issue tracking/wiki/etc  
    21 # system. 
    22 # 
    23 # It should be called from the 'pre-commit' script in Subversion, such as 
    24 # via: 
    25 # 
    26 #  REPOS="$1" 
    27 #  TXN="$2" 
    28 #  TRAC_ENV="/somewhere/trac/project/" 
    29 # 
    30 # /usr/bin/python /usr/local/src/trac/contrib/trac-uni-commit-hook \ 
    31 #  -d "$REPOS" \ 
    32 #  -p "$TRAC_ENV"  \ 
    33 #  -t "$TXN" 
    34 # 
    35 #  
    36 # It should be called from the 'post-commit' script in Subversion, such as 
    37 # via: 
    38 # 
    39 # REPOS="$1" 
    40 # REV="$2" 
    41 # TRAC_ENV='/somewhere/trac/project/' 
    42 # 
    43 # /usr/bin/python /usr/local/src/trac/contrib/trac-uni-commit-hook \ 
    44 #  -d "$REPOS" \ 
    45 #  -p "$TRAC_ENV"  \ 
    46 #  -r "$REV" 
    47 # 
    4818 
    4919import re 
     
    8454# 
    8555ticket_cmd_pattern = re.compile(r'^(?P<action>refs|closes|fixes).?#(?P<ticket>[0-9]+)', re.IGNORECASE) 
    86 changeset_cmd_pattern = re.compile(r'^(?P<action>delivers|brings)(?P<force>.?)\s+((\[(?P<first>\d+)\])(:\[(?P<second>\d+)\])?)?', re.IGNORECASE) 
     56changeset_cmd_pattern = re.compile(r'^(?P<action>delivers|brings)(?P<force>\!)?\s+((\[(?P<first>\d+)\])(\s*:\s*\[(?P<second>\d+)\])?)?', re.IGNORECASE) 
    8757create_pattern = re.compile(r'^(?P<action>creates)', re.IGNORECASE) 
    88 terminate_pattern = re.compile(r'^(?P<action>terminates)(?P<force>.?)\s', re.IGNORECASE) 
     58terminate_pattern = re.compile(r'^(?P<action>terminates)(?P<force>\!)?\s', re.IGNORECASE) 
    8959admin_pattern = re.compile(r'^(?P<action>admins)', re.IGNORECASE) 
    9060 
     
    9969# SVN directories 
    10070# 
    101 branch_directory = '/branches' 
     71dev_branch_dirs = ['/sandboxes', '/branches'] 
    10272trunk_directory = '/trunk' 
    103 configpath = 'C:/Var/svn/config/access.conf' 
     73configpath = '/local/var/svn/config/access.conf' 
    10474 
    10575# 
     
    137107        self.author = self._get_author() 
    138108        self.log = self._get_log() 
    139         os.environ['PYTHON_EGG_CACHE'] = \ 
    140             os.path.join(project, '..', '..', 'eggcache') 
    141109        if not os.path.isdir(os.environ['PYTHON_EGG_CACHE']): 
    142110            raise AssertionError("Invalid egg cache directory: %s" % \ 
     
    148116                  r'(?:/(?P<path>.*))?$') 
    149117        self.bcre = re.compile(bre) 
     118         
     119        # Nearly empty log message 
     120        if not self.log: 
     121            print>>sys.stderr, 'Log message is invalid' 
     122            self.finalize(ERROR)   
    150123 
    151124        # Administration commit 
     
    158131        terminate = terminate_pattern.search(self.log) 
    159132        if terminate:             
    160             rc = self._cmd_terminates(terminate.group('force')
     133            rc = self._cmd_terminates(terminate.group('force') and True
    161134            self.finalize(OK) 
    162135 
     
    174147                func = getattr(self, CommitHook._changeset_cmds[cmd]) 
    175148                rc = func(chgset_cmd.group('first'), chgset_cmd.group('second'),  
    176                           chgset_cmd.group('force')
     149                          chgset_cmd.group('force') and True
    177150                self.finalize(rc) 
    178151            else: 
     
    189162                self.finalize(rc) 
    190163            else: 
    191                 print>>sys.stderr, 'No supported action specified in log message !' 
     164                print>>sys.stderr, 'No supported action in log message !' 
    192165                self.finalize(ERROR) 
    193166 
    194167        # Unrecognized log message 
    195         print>>sys.stderr, 'No action specified in log message !' 
     168        print>>sys.stderr, 'No known action in log message !' 
    196169        self.finalize(ERROR)   
    197170 
     
    270243        ticket_dict = {} 
    271244        for rev in revisions: 
    272             bring_prop = self.proxy.get_revision_property(int(rev), bring_prop_name) 
     245            bring_prop = self.proxy.get_revision_property(int(rev), \ 
     246                                                          bring_prop_name) 
    273247            if bring_prop and len(bring_prop) > 0: 
    274248                bring_revs = bring_prop.split(',') 
     
    295269        return is_open 
    296270 
     271    def _is_admin(self, author): 
     272        ''' 
     273        Verify whether the author has administrator priviledges 
     274        ''' 
     275        config = ConfigParser() 
     276        if not os.path.isfile(configpath): 
     277            raise AssertionError('Unable to find Subversion ACL for admins') 
     278        config.read(configpath) 
     279        admins = config.get('groups','admins') 
     280        if not admins: 
     281            raise AssertionError('Unable to retrieve Subversion ACL for admins') 
     282        if not author.lower() in [s.strip() for s in admins.lower().split(',')]: 
     283            return False 
     284        return True 
     285 
     286    def _is_dev_branch(self, dir_path): 
     287        ''' 
     288        Tell whether a directory is located inside a development branch or not 
     289        ''' 
     290        for dev_br in dev_branch_dirs: 
     291            if dir_path[:len(dev_br)] == dev_br: 
     292                return True 
     293        return False 
     294 
    297295 
    298296class PreCommitHook(CommitHook): 
     
    323321        ''' 
    324322        log = self.proxy.get_txn_log_message() 
     323        if len(log) < 2: 
     324            return None 
     325        # Be sure the first letter is uppercased 
     326        log = log[0].upper() + log[1:] 
    325327        return log 
    326328 
     329    def _update_log(self, log): 
     330        ''' 
     331        Update the transaction log message 
     332        ''' 
     333        self.proxy.set_txn_log_message(log) 
     334 
    327335    def _is_txn_branch_directory(self): 
    328336        ''' 
    329337        Check if the directory of the transaction is a branch directory 
    330         (located under /branches
     338        (located under the branches directory
    331339        ''' 
    332340        dst_branch = self.proxy.find_txn_branch(self.bcre) 
    333         is_branch = dst_branch[:len(branch_directory)] == branch_directory 
    334         return is_branch 
     341        return self._is_dev_branch(dst_branch) 
    335342 
    336343    def finalize(self, result): 
     344        if OK == result: 
     345            self._update_log(self.log) 
    337346        sys.exit(result)  
    338347 
     
    340349        ''' 
    341350        Administrative commit 
    342         Check that the author has administrator rights 
    343         ''' 
    344         config = ConfigParser() 
    345         if not os.path.isfile(configpath): 
    346             print >>sys.stderr, 'Unable to find Subversion ACL for admins' 
    347             self.finalize(ERROR) 
    348         config.read(configpath) 
    349         admins = config.get('groups','admins') 
    350         if not admins: 
    351             print >>sys.stderr, 'Unable to retrieve Subversion ACL for admins' 
    352             self.finalize(ERROR) 
    353         if not self.author.lower() in map(string.strip, admins.lower().split(',')): 
     351        ''' 
     352        if not self._is_admin(self.author): 
    354353            print >>sys.stderr, 'Only administrator can execute admin commits' 
    355354            self.finalize(ERROR) 
     
    368367            self.finalize(ERROR) 
    369368        dstbranch = self.proxy.find_txn_branch(self.bcre) 
    370         if dstbranch[:len(branch_directory)] != branch_directory
     369        if not self._is_dev_branch(dstbranch)
    371370            print >> sys.stderr, 'Cannot create a new branch outside %s' \ 
    372                                   % branch_directory 
     371                                  % dev_branch_dirs 
    373372            self.finalize(ERROR) 
    374373        return OK 
     
    383382            item = change_gen.next() 
    384383        except StopIteration: 
    385             print >> sys.stderr, "No deleted path in the submitted revision" 
     384            print >> sys.stderr, 'No deleted path in the submitted revision' 
    386385            self.finalize(ERROR) 
    387386        try: 
     
    390389            pass 
    391390        else: 
    392             print >> sys.stderr, "Termination of more than one branch is not allowed" 
     391            print >> sys.stderr, 'Termination of more than one branch is not ' \ 
     392                                 'allowed' 
    393393            self.finalize(ERROR) 
    394394        (path, change) = item 
     
    397397            self.finalize(ERROR) 
    398398        dstbranch = self.proxy.find_txn_branch(self.bcre) 
    399         if dstbranch[:len(branch_directory)] != branch_directory
     399        if not self._is_dev_branch(dstbranch)
    400400            print >> sys.stderr, 'Cannot terminates a non-branch dir (%s)' \ 
    401                                   % branch_directory 
    402             self.finalize(ERROR) 
    403         if force != '!'
     401                                  % dev_branch_dirs 
     402            self.finalize(ERROR) 
     403        if not force
    404404            youngest = self.proxy.get_youngest_path_revision(path)  
    405405            # now checks that the deleter is the creator of the branch 
     
    428428            self.finalize(ERROR) 
    429429        if not self._is_txn_branch_directory(): 
    430             print >> sys.stderr, 'Cannot apply changes to a non-branch dir (%s)' \ 
    431                                   % branch_directory 
     430            print >> sys.stderr, 'Cannot apply changes to a non-branch dir' \ 
     431                                 ' (%s)' % dev_branch_dirs 
    432432            self.finalize(ERROR) 
    433433        return OK 
     
    489489            print >> sys.stderr, "Revisions %s %s %s" % (rev1, rev2, revisions) 
    490490            self.finalize(ERROR) 
     491 
    491492        # On error, the transaction is destroyed, so it is safe to apply the 
    492493        # property even if the hook fails. 
     
    498499            print >> sys.stderr, 'Unable to locate delivery destination' 
    499500            self.finalize(ERROR) 
     501 
     502        # Ensure the branch is not delivered to self 
    500503        branch1 = self.proxy.find_revision_branch(int(rev1), self.bcre) 
    501504        if dstbranch == branch1: 
    502505            print >> sys.stderr, 'Cannot deliver to self (%s -> %s)' % \ 
    503506                                  (branch1, dstbranch) 
     507            self.finalize(ERROR) 
     508 
     509        # Ensure that the 'branch creation' revision is not selected as a source 
     510        print >> sys.stderr, "BRANCH1: (%s) [%s]" % (rev1, branch1) 
     511        brevs = [h[0] for h in self.proxy.get_history(int(rev1), branch1, None)] 
     512        if rev1 == brevs[0]: 
     513            print >> sys.stderr, \ 
     514                'Cannot deliver the initial branch revision (%d)' % rev1 
    504515            self.finalize(ERROR) 
    505516 
     
    523534                #    'as the latest CC export (rev %d on %s) has not been ' \ 
    524535                #    'tagged and imported back into SVN' % (rev, exportval) 
    525                 #    if force != '!'
     536                #    if not force
    526537                #        # Aborts if no force mode is specified 
    527538                #        self.finalize(ERROR) 
     
    530541        # @todo there's probably a better way to format this 
    531542        tickets = self._collect_tickets(revisions) 
    532         full_log = self.log 
    533         full_log += '\n' 
     543        full_log = self.log.decode('utf8') 
     544        full_log += u'\n' 
    534545        for tkt_id in tickets: 
    535546            ticket = Ticket(self.env, tkt_id) 
    536             full_log += '\n* #%s: %s\n' % (tkt_id, ticket['summary']) 
    537             full_log += '\n'.join( [ "%s - %s" % (revi[0], revi[1]) for revi in tickets[tkt_id] ]) 
    538             full_log += '\n' 
    539         # print >> sys.stderr, full_log 
    540         self.proxy.set_txn_log_message(str(full_log)) 
    541  
     547            full_log += u'\n* #%s: %s\n' % (tkt_id, ticket['summary']) 
     548            full_log += u'\n'.join([u'  %s' % (revi[1].decode('utf8')) \ 
     549                                    for revi in tickets[tkt_id]]) 
     550            full_log += u'\n' 
     551        full_log = full_log[0].upper() + full_log[1:] 
     552        self.log = full_log.encode('utf8') 
     553  
    542554        # Check if there is a next milestone defined in Trac 
    543555        # This is a bit too conservative, as a next milestone is only needed if 
  • revtreeplugin/0.11/hooks/trac-revprop-hook

    • Property svn:eol-style set to native
    r2638 r3581  
    33# trac-revprop-hook 
    44# ---------------------------------------------------------------------------- 
    5 # Copyright (c) 2007 Emmanuel Blot 
     5# Copyright (c) 2007-2008 Emmanuel Blot 
    66# ---------------------------------------------------------------------------- 
    77 
     
    3535ERROR = 1 
    3636 
    37 configpath = 'C:/Var/svn/config/access.conf' 
     37configpath = '/local/var/svn/config/access.conf' 
     38 
     39changeset_cmd_pattern = r'^(?P<action>delivers|brings)(?P<force>\!)?\s+((\[(?P<first>\d+)\])(:\[(?P<second>\d+)\])?)?' 
    3840 
    3941class RevpropHook(object): 
     
    4244    def __init__(self, post, project, rev, name, value,  
    4345                 action=None, author=None, repos=None): 
    44         os.environ['PYTHON_EGG_CACHE'] = os.path.join(project, '..', '..', 'eggcache') 
    4546        if not os.path.isdir(os.environ['PYTHON_EGG_CACHE']): 
    4647            raise AssertionError("Invalid egg cache directory: %s" % os.environ['PYTHON_EGG_CACHE']); 
     
    8990 
    9091    def _verify_log_msg(self): 
    91         regex = re.compile(r'^(?P<action>delivers|brings).?\s+((\[(?P<first>\d+)\])(:\[(?P<second>\d+)\])?)?',  
    92                            re.IGNORECASE) 
     92        regex = re.compile(changeset_cmd_pattern, re.IGNORECASE) 
    9393        self.proxy = RepositoryProxy(self.repospath) 
    9494        oldlog = self.proxy.get_revision_log_message(self.rev) 
     
    9696        if oldmo: 
    9797            newmo = regex.search(self.value) 
    98             if (not newmo) or \ 
    99                (oldmo.group('first') != newmo.group('first')) or \ 
     98            if (not newmo): 
     99                print >> sys.stderr, \ 
     100                    'Missing message:\n  was: "%s"' % oldlog.split('\n')[0] 
     101                sys.exit(-ERROR)  
     102            if (oldmo.group('first') != newmo.group('first')) or \ 
    100103               (oldmo.group('second') != newmo.group('second')): 
    101                 print >> sys.stderr, \ 
    102                     'Original parameters should be kept unaltered:\n  "%s"' % \ 
    103                                      oldlog.split('\n')[0] 
    104                 sys.exit(-ERROR)  
     104                if not self._is_admin(self.author) or not newmo.group('force'): 
     105                    print >> sys.stderr, \ 
     106                        'Original parameters should be kept unaltered:\n' \ 
     107                        '  "%s"' % oldlog.split('\n')[0] 
     108                    sys.exit(-ERROR)  
    105109 
    106110    def _verify_rth_deliver(self): 
     
    134138        repos = self.env.get_repository() 
    135139        repos.sync_changeset(self.rev) 
     140 
     141    def _is_admin(self, author): 
     142        ''' 
     143        Verify whether the author has administrator priviledges 
     144        ''' 
     145        config = ConfigParser() 
     146        if not os.path.isfile(configpath): 
     147            raise AssertionError('Unable to find Subversion ACL for admins') 
     148        config.read(configpath) 
     149        admins = config.get('groups','admins') 
     150        if not admins: 
     151            raise AssertionError('Unable to retrieve Subversion ACL for admins') 
     152        if not author.lower() in [s.strip() for s in admins.lower().split(',')]: 
     153            return False 
     154        return True 
    136155 
    137156