Skip to content

Instantly share code, notes, and snippets.

@berngp
Last active August 29, 2015 14:00
Show Gist options
  • Save berngp/12d70f2f7aff0f16a33f to your computer and use it in GitHub Desktop.
Save berngp/12d70f2f7aff0f16a33f to your computer and use it in GitHub Desktop.
Mesos support/post-reviews.py with support for PRTool 0.5.x and 0.6.x
#!/usr/bin/env python
# This is a wrapper around the 'post-review'/'rbt' tool provided by
# Review Board. This is currently used by Apache Mesos development.
#
# What does this do?
# It provides the ability to send a review for each commit on the
# current branch.
#
# Why is that useful?
# No one likes a 5000 line review request. Using this tool forces one
# to create logical commits which can be reviewed independently.
#
# How do I use it?
# First install 'RBTools' from Review Board.
# http://www.reviewboard.org/downloads/rbtools/
#
# $ cd /path/to/mesos
# $ [ do some work on your branch off of master, make commit(s) ]
# $ ./support/post-reviews.py --server=https://reviews.apache.org \
# --tracking-branch=origin/master --target-groups=mesos --open
#
# NOTE: post-reviews is currently specific to Mesos development,
# but can easily be adapted for other projects.
import atexit
import os
import sys
from subprocess import *
def readline(prompt):
try:
return raw_input(prompt)
except KeyboardInterrupt:
sys.exit(1)
def execute(command, ignore_errors=False):
process = Popen(command,
stdin=PIPE,
stdout=PIPE,
stderr=STDOUT,
shell=False)
data = process.stdout.read()
status = process.wait()
if status != 0 and not ignore_errors:
cmdline = ' '.join(command) if isinstance(command, list) else command
print 'Failed to execute: \'' + cmdline + '\':'
print data
sys.exit(1)
elif status != 0:
return None
return data
def do_post_review(review_request_id,
repository,
tracking_branch,
revision_range,
args=[]):
# Choose 'post-review' if available, otherwise choose 'rbt'.
if execute(['type', 'post-review'], ignore_errors=True):
command = ['post-review',
'--repository-url=' + repository,
'--tracking-branch=' + tracking_branch,
'--revision-range=' + revision_range]
if review_request_id is not None:
command.append('--review-request-id=' + review_request_id)
command += args
elif execute(['type', 'rbt'], ignore_errors=True):
print 'Please install RBTools 5.x e.g. `easy_install rbtools==0.5.7`'
sys.exit(1)
# command = ['rbt',
# 'post',
# '--repository-url=' + repository,
# '--tracking-branch=' + tracking_branch]
# if review_request_id is not None:
# command.append('--review-request-id=' + review_request_id)
# command += args
# command.append(revision_range)
else:
print 'Please install RBTools before proceeding'
sys.exit(1)
return execute(command).strip()
# TODO(benh): Make sure this is a git repository, apologize if not.
# Don't do anything if people have uncommitted changes.
diff_stat = execute(['git', 'diff', '--shortstat']).strip()
if diff_stat:
print 'Please commit or stash any changes before using post-reviews!'
sys.exit(1)
top_level_dir = execute(['git', 'rev-parse', '--show-toplevel']).strip()
repository = 'git://git.apache.org/mesos.git'
parent_branch = 'master'
branch_ref = execute(['git', 'symbolic-ref', 'HEAD']).strip()
branch = branch_ref.replace('refs/heads/', '', 1)
# do not work on master branch
if branch == "master":
print "We're expecting you to be working on another branch from master!"
sys.exit(1)
temporary_branch = '_post-reviews_' + branch
# Always delete the temporary branch.
atexit.register(lambda:
execute(['git', 'branch', '-D', temporary_branch], True))
# Always put us back on the original branch.
atexit.register(lambda:
execute(['git', 'checkout', branch]))
merge_base = execute(['git', 'merge-base', parent_branch, branch_ref]).strip()
print 'Running RBTool across all of ...'
call(['git',
'--no-pager',
'log',
'--pretty=format:%Cred%H%Creset '
'-%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset',
merge_base + '..HEAD'])
log = execute([
'git',
'--no-pager',
'log',
'--pretty=oneline',
'--reverse', merge_base + '..HEAD']).strip()
if len(log) <= 0:
print "No new changes compared with master branch!"
sys.exit(1)
shas = []
for line in log.split('\n'):
sha = line.split()[0]
shas.append(sha)
previous = 'master'
for i in range(len(shas)):
sha = shas[i]
execute(['git', 'branch', '-D', temporary_branch], True)
message = execute(['git',
'--no-pager',
'log',
'--pretty=format:%B',
previous + '..' + sha])
review_request_id = None
if message.find('Review: ') != -1:
url = message[(message.index('Review: ') + len('Review: ')):].strip()
# TODO(benh): Handle bad (or not Review Board) URLs.
review_request_id = os.path.basename(url.strip('/'))
# Show the commit.
if review_request_id is None:
print '\nCreating diff of:\n'
call(['git',
'--no-pager',
'log',
'--pretty=format:%Cred%H%Creset -%C(yellow)%d%Creset %s',
previous + '..' + sha])
else:
print '\nUpdating diff of:\n'
call(['git',
'--no-pager',
'log',
'--pretty=format:%Cred%H%Creset '
'-%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset',
previous + '..' + sha])
# Show the "parent" commit(s).
print '\n... with parent diff created from:\n'
call(['git',
'--no-pager',
'log',
'--pretty=format:%Cred%H%Creset '
'-%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset',
parent_branch + '..' + previous])
try:
raw_input('\nPress enter to continue or \'Ctrl-C\' to skip.\n')
except KeyboardInterrupt:
i = i + 1
previous = sha
continue
revision_range = previous + ':' + sha
if review_request_id is None:
output = do_post_review(None,
repository,
parent_branch,
revision_range,
sys.argv[1:])
else:
output = do_post_review(review_request_id,
repository,
parent_branch,
revision_range,
sys.argv[1:])
print output
if review_request_id is not None:
i = i + 1
previous = sha
continue
lines = output.split('\n')
url = lines[len(lines) - 1]
url = url.strip('/')
# Construct new commit message.
message = message + '\n' + 'Review: ' + url + '\n'
execute(['git', 'checkout', '-b', temporary_branch])
execute(['git', 'reset', '--hard', sha])
execute(['git', 'commit', '--amend', '-m', message])
# Now rebase all remaining shas on top of this amended commit.
j = i + 1
old_sha = execute(['cat',
os.path.join(top_level_dir,
'.git/refs/heads',
temporary_branch)]).strip()
previous = old_sha
while j < len(shas):
execute(['git', 'checkout', shas[j]])
execute(['git', 'rebase', temporary_branch])
# Get the sha for our detached HEAD.
new_sha = execute(['git',
'--no-pager',
'log',
'--pretty=format:%H',
'-n',
'1',
'HEAD']).strip()
execute(['git',
'update-ref',
'refs/heads/' + temporary_branch,
new_sha,
old_sha])
old_sha = new_sha
shas[j] = new_sha
j = j + 1
# Okay, now update the actual branch to our temporary branch.
new_sha = old_sha
old_sha = execute(['cat',
os.path.join(top_level_dir,
'.git/refs/heads',
branch)]).strip()
execute(['git', 'update-ref', 'refs/heads/' + branch, new_sha, old_sha])
i = i + 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment