]>
git.rmz.io Git - dotfiles.git/blob - bin/hooks/svn-hook-postcommit-review
   3 # svn-hook-postcommit-review 
   4 # This script should be invoked from the subversion post-commit hook like this: 
   8 # /usr/bin/python /some/path/svn-hook-postcommit-review "$REPOS" "$REV" || exit 1 
  10 # Searches the commit message for text in the form of: 
  11 #   publish review - publishes a review request 
  12 #   draft review - creates a draft review request 
  14 # The space before 'review' may be ommitted. 
  16 # The log message is interpreted for review request parameters: 
  17 #    summary = up to first period+space, first new-line, or 250 chars 
  18 #    description = entire log message 
  19 #    existing review updated if log message includes 'update review:[0-9]+' 
  20 #    bugs added to review if log message includes commands as defined in 
  21 #      supported_ticket_cmds 
  23 # By default, the review request is created out of a diff between the current 
  24 # revision (M) and the previous revision (M-1). 
  26 # To create a diff that spans multiple revisions, include 
  27 # 'after revision:[0-9]+' in the log message. 
  29 # To limit the diff to changes in a certain path (e.g. a branch), include 
  30 # 'base path:"<path>"' in the log message.  The path must be relative to 
  31 # the root of the repository and be surrounded by single or double quotes. 
  33 # An example commit message is: 
  35 #    Changed blah and foo to do this or that.  Publish review ticket:1 
  36 #      update review:2 after revision:3 base path:'internal/trunk/style'. 
  38 # This would update the existing review 2 with a diff of changes to files under 
  39 # the style directory between this commit and revision 3.  It would place 
  40 # the entire log message in the review summary and description, and put 
  41 # bug id 1 in the bugs field. 
  43 # This script may only be run from outside a working copy. 
  47 # User configurable variables 
  50 # Path to post-review script 
  52 # Username and password for Review Board user that will be connecting 
  53 # to create all review requests.  This user must have 'submit as' 
  54 # privileges, since it will submit requests in the name of svn committers. 
  56 PASSWORD 
= 'TYxxcGm337FtubqN' 
  58 # If true, runs post-review in debug mode and outputs its diff 
  62 # end user configurable variables 
  73 # list of trac commands from trac-post-commit-hook.py. 
  74 # numbers following these commands will be added to the bugs 
  75 # field of the review request. 
  76 supported_ticket_cmds 
= {'review':         '_cmdReview', 
  77                          'publishreview':  '_cmdReview', 
  78                          'publish review': '_cmdReview', 
  79                          'draftreview':    '_cmdReview', 
  80                          'draft review':   '_cmdReview'} 
  82 ticket_prefix 
= '(?:#|(?:ticket|issue|bug)[: ]?)' 
  83 ticket_reference 
= ticket_prefix 
+ '[0-9]+' 
  84 ticket_command 
= (r
'(?P<action>[A-Za-z]*).?' 
  85                   '(?P<ticket>%s(?:(?:[, &]*|[ ]?and[ ]?)%s)*)' % 
  86                   (ticket_reference
, ticket_reference
)) 
  88 def execute(command
, env
=None, ignore_errors
=False): 
  90     Utility function to execute a command and return the output. 
  91     Derived from Review Board's post-review script. 
  94         env
.update(os
.environ
) 
  98     p 
= subprocess
.Popen(command
, 
  99                          stdin 
= subprocess
.PIPE
, 
 100                          stdout 
= subprocess
.PIPE
, 
 101                          stderr 
= subprocess
.STDOUT
, 
 103                          close_fds 
= sys
.platform
.startswith('win'), 
 104                          universal_newlines 
= True, 
 106     data 
= p
.stdout
.read() 
 108     if rc 
and not ignore_errors
: 
 109         sys
.stderr
.write('Failed to execute command: %s\n%s\n' % (command
, data
)) 
 115     if len(sys
.argv
) != 3: 
 116         sys
.stderr
.write('Usage: %s <repos> <rev>\n' % sys
.argv
[0]) 
 122     # verify that rev parameter is an int 
 126         sys
.stderr
.write("Parameter <rev> must be an int, was given %s\n" % rev
) 
 129     # get the svn file system object 
 130     fs_ptr 
= svn
.repos
.svn_repos_fs(svn
.repos
.svn_repos_open( 
 131             svn
.core
.svn_path_canonicalize(repos
))) 
 133     # get the log message 
 134     log 
= svn
.fs
.svn_fs_revision_prop(fs_ptr
, int(rev
), 
 135                                     svn
.core
.SVN_PROP_REVISION_LOG
) 
 137     # error if log message is blank 
 138     if len(log
.strip()) < 1: 
 139         sys
.stderr
.write("Log message is empty, no review request created\n") 
 143     author 
= svn
.fs
.svn_fs_revision_prop(fs_ptr
, int(rev
), 
 144                                        svn
.core
.SVN_PROP_REVISION_AUTHOR
) 
 146     # error if author is blank 
 147     if len(author
.strip()) < 1: 
 148         sys
.stderr
.write("Author is blank, no review request created\n") 
 151     # check whether to create a review, based on presence of word 
 152     # 'review' with prefix 
 153     review 
= r
'(?:publish|draft)(?: )?review' 
 154     if not re
.search(review
, log
, re
.M | re
.I
): 
 155         print 'No review requested' 
 158     # check for update to existing review 
 159     m 
= re
.search(r
'update(?: )?review:([0-9]+)', log
, re
.M | re
.I
) 
 161         reviewid 
= '--review-request-id=' + m
.group(1) 
 165     # check whether to publish or leave review as draft 
 166     if re
.search(r
'draft(?: )?review', log
, re
.M | re
.I
): 
 171     # get previous revision number -- either 1 prior, or 
 172     # user-specified number 
 173     m 
= re
.search(r
'after(?: )?revision:([0-9]+)', log
, re
.M | re
.I
) 
 177         prevrev 
= int(rev
) - 1 
 179     # check for an explicitly-provided base path (must be contained 
 181     m 
= re
.search(r
'base ?path:[\'"]([^\'"]+)[\'"]', log, re.M | re.I) 
 183         base_path = m.group(1) 
 187     # get bug numbers referenced in this log message 
 188     ticket_command_re = re.compile(ticket_command) 
 189     ticket_re = re.compile(ticket_prefix + '([0-9]+)') 
 192     ticket_cmd_groups = ticket_command_re.findall(log) 
 193     for cmd, tkts in ticket_cmd_groups: 
 194         funcname = supported_ticket_cmds.get(cmd.lower(), '') 
 196             for tkt_id in ticket_re.findall(tkts): 
 197                 ticket_ids.append(tkt_id) 
 200         bugs = '--bugs-closed=' + ','.join(ticket_ids) 
 204     # summary is log up to first period+space / first new line / first 250 chars 
 205     # (whichever comes first) 
 206     summary = '--summary=' + log[:250].splitlines().pop(0).split('. ').pop(0) 
 208     # other parameters for postreview 
 209     repository_url  = '--repository-url=file://' + repos 
 210     password        = '--password=' + PASSWORD 
 211     username        = '--username=' + USERNAME 
 212     description     = "--description
=(In 
[%s]) %s" % (rev, log) 
 213     submitas        = '--submit-as=' + author 
 214     revision        = '--revision-range=%s:%s' % (prevrev, rev) 
 217     args = [repository_url, username, password, publish, 
 218             submitas, revision, base_path, reviewid] 
 220     # filter out any potentially blank args, which will confuse post-review 
 221     args = [i for i in args if len(i) > 1] 
 223     # if not updating an existing review, add extra arguments 
 224     if len(reviewid) == 0: 
 225         args += [summary, description, bugs] 
 228         args += ['-d', '--output-diff'] 
 229         print [os.path.join(POSTREVIEW_PATH, 'post-review')] + args 
 231     # Run Review Board post-review script 
 232     data = execute([os.path.join(POSTREVIEW_PATH, 'post-review')] + args, 
 233                    env = {'LANG': 'en_US.UTF-8'}) 
 238 if __name__ == '__main__':