Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TASK] implement autorebase before merge (RT-2870) #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def clean(haystack, needle=""):
"orgname" : "", # The name of ripple's github organization
"cibotnames" : [], # The names of ripple's CI bots
"hookurl" : "", # The url of the server file for hooking into
"mergestring": "@ripplebot merge", # What string tells the bot to merge the pull request
"votecount" : 2, # The number of LGTM votes required to merge
"recvotes" : 3, # How many of those have to be after the most recent commit
"message" : "Ready to merge: Travis build checks out, most recent commit looks good to <recvoters>.", # The message displayed by the bot on merge
Expand Down Expand Up @@ -74,8 +75,8 @@ def clean(haystack, needle=""):
},
"travis" : True, # Whether or not to check Travis for build status
"orgvote" : False, # Whether or not the votes of all organization members should count
"enabled" : False, # Whether or not the bot is enabled
"merge" : False, # Whether or not the bot should actually merge, or just comment
"enabled" : True, # Whether or not the bot is enabled
"merge" : True, # Whether or not the bot should actually merge, or just comment
"debug" : True # Turns on and off verbose debug output
}

Expand Down
63 changes: 57 additions & 6 deletions gitbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
from __future__ import print_function
import github
import string
import subprocess
from urlparse import urlparse
import sys
import traceback
import requests
import os
import shutil

# Setting Up:

Expand Down Expand Up @@ -54,10 +61,12 @@ def status(pull, params):
return False

def check(commentlist, params):
"""Checks That At Least votecount Members Have Commented LGTM And None Commented VETO."""
"""Checks That At Least votecount Members Have Commented LGTM, None Commented VETO, And At Least One Requested the Gitbot Merge the Pull Request."""
printdebug(params, " Checking comments...")
votes = {}
recvotes = {}
botmerge = False
mergeforuser = ""
if params["creator"] in params["members"]:
votes[params["creator"]] = 1 # If the creator is a member, give them a vote
printdebug(params, " Got LGTM vote from creator "+params["creator"]+".")
Expand All @@ -67,6 +76,9 @@ def check(commentlist, params):
for user, comment, date in commentlist:
if user in params["members"]:
voted = True
if params["mergestring"] in comment:
botmerge = True
mergeforuser = user
if startswithany(comment, params["lgtms"]): # If a member commented LGTM, give them a vote
votes[user] = 1
printdebug(params, " Got LGTM vote from "+user+".")
Expand All @@ -83,15 +95,44 @@ def check(commentlist, params):
if voted:
recvotes[user] = votes[user]
printdebug(params, " Vote qualifies as recent.")
if sum(votes.values()) >= params["votecount"] and sum(recvotes.values()) >= params["recvotes"]:
printdebug(params, " Found no VETO votes, at least "+str(params["votecount"])+" LGTM votes, and at least "+str(params["recvotes"])+" recent LGTM votes.")
if botmerge and sum(votes.values()) >= params["votecount"] and sum(recvotes.values()) >= params["recvotes"]:
printdebug(params, " Found no VETO votes, at least "+str(params["votecount"])+" LGTM votes, at least "+str(params["recvotes"])+" recent LGTM votes, and user "+mergeforuser+" requested the bot merge this pull request .")
params["voters"] = ", ".join(sorted(votes.keys()))
params["recvoters"] = ", ".join(sorted(recvotes.keys()))
return messageproc(params, params["message"])
else:
printdebug(params, " Found fewer than "+str(params["votecount"])+" LGTM votes, a VETO vote, or fewer than "+str(params["recvotes"])+" recent LGTM votes.")
printdebug(params, " Found fewer than "+str(params["votecount"])+" LGTM votes, a VETO vote, fewer than "+str(params["recvotes"])+" recent LGTM votes, or no user requesting the bot to merge.")
return False

def autorebase(pullpath, ripple_url):
error = False
res = requests.get(pullpath) # Call github API to get raw JSON
jsondata = res.json()
url = jsondata["head"]["repo"]["clone_url"]
branch = jsondata["head"]["ref"]
path = urlparse(url).path.split("/")
len = len(path)
dir = path[len-1].split(".")[0] # remove trailing ".git" from directory
os.chdir(os.environ["HOME"]) # do this to be sure to clone from home directory
try:
subprocess.check_call(["git", "clone", url])
os.chdir(dir)
subprocess.check_call(["git", "checkout", "-b", branch])
subprocess.check_call(["git", "remote", "add", "ripple", ripple_url])
subprocess.check_call(["git", "fetch", "ripple"])
subprocess.check_call(["git", "config", "--global", "push.default", "simple"])
subprocess.check_call(["git", "rebase", "ripple/master"]) # try to rebase and push to create a new pull request
subprocess.check_call(["git", "push", "-f"])
except subprocess.CalledProcessError:
error = True
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
message = ''.join('- ' + line for line in lines)
printdebug(specparams, message)
os.chdir(os.environ["HOME"]) # change back to home directory for clean up
shutil.rmtree(dir)
return error

# Utility Functions:

def startswithany(inputstring, inputlist):
Expand Down Expand Up @@ -293,8 +334,18 @@ def main(params):
pull.create_issue_comment(message) # Create a comment with the middleware function's result
printdebug(specparams, " Pull request commented on.")
if specparams["merge"]:
pull.merge(message) # Merge using the middleware function's result as the description
printdebug(specparams, " Pull request merged.")
pullnumber = pull.number
pullpath = "https://api.github.com/repos/ripple/" + repo.name + "/pulls/" + str(pullnumber)
error = autorebase(pullpath, repo.clone_url) # try to rebase the branch before merging the pull request
if error == False: # no error rebasing the branch
if not pull.is_merged() and pull.mergeable: # safety check
pull.merge(message) # Merge using the middleware function's result as the description
printdebug(specparams, " Pull request " + pullpath + " merged.")
else:
printdebug(specparams, " Pull request not merged due to conflict." + pull.user.login + " please check your pull request.")
else:
printdebug(specparams, " Pull request not merged due to error." + pull.user.login + " please check your pull request.")


# Cleaning Up:

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ PyGithub==1.25.0
Werkzeug==0.9.6
gunicorn==18.0
wsgiref==0.1.2
requests