Skip to content

Commit da24c1a

Browse files
wesmxhochy
authored andcommitted
ARROW-339: Python 3 compatibility in merge_arrow_pr.py
Author: Wes McKinney <wes.mckinney@twosigma.com> Closes #188 from wesm/ARROW-339 and squashes the following commits: 1f3617f [Wes McKinney] Remove cherry-picking cruft 6b99632 [Wes McKinney] Python 3 compatibility in merge_arrow_pr.py
1 parent 6178bf7 commit da24c1a

File tree

1 file changed

+88
-105
lines changed

1 file changed

+88
-105
lines changed

dev/merge_arrow_pr.py

Lines changed: 88 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,24 @@
1717
# limitations under the License.
1818
#
1919

20-
# Utility for creating well-formed pull request merges and pushing them to Apache.
20+
# Utility for creating well-formed pull request merges and pushing them to
21+
# Apache.
2122
# usage: ./apache-pr-merge.py (see config env vars below)
2223
#
2324
# This utility assumes you already have a local Arrow git clone and that you
2425
# have added remotes corresponding to both (i) the Github Apache Arrow mirror
2526
# and (ii) the apache git repo.
2627

27-
import json
2828
import os
2929
import re
3030
import subprocess
3131
import sys
32-
import tempfile
33-
import urllib2
32+
import requests
3433
import getpass
3534

35+
from six.moves import input
36+
import six
37+
3638
try:
3739
import jira.client
3840
JIRA_IMPORTED = True
@@ -42,8 +44,8 @@
4244
# Location of your Arrow git clone
4345
ARROW_HOME = os.path.abspath(__file__).rsplit("/", 2)[0]
4446
PROJECT_NAME = ARROW_HOME.rsplit("/", 1)[1]
45-
print "ARROW_HOME = " + ARROW_HOME
46-
print "PROJECT_NAME = " + PROJECT_NAME
47+
print("ARROW_HOME = " + ARROW_HOME)
48+
print("PROJECT_NAME = " + PROJECT_NAME)
4749

4850
# Remote name which points to the Gihub site
4951
PR_REMOTE_NAME = os.environ.get("PR_REMOTE_NAME", "apache-github")
@@ -65,46 +67,38 @@
6567

6668

6769
def get_json(url):
68-
try:
69-
from urllib2 import urlopen, Request
70-
env_var = 'ARROW_GITHUB_API_TOKEN'
71-
72-
if env_var in os.environ:
73-
token = os.environ[env_var]
74-
request = Request(url)
75-
request.add_header('Authorization', 'token %s' % token)
76-
response = urlopen(request)
77-
else:
78-
response = urlopen(url)
79-
return json.load(response)
80-
except urllib2.HTTPError as e:
81-
print "Unable to fetch URL, exiting: %s" % url
82-
sys.exit(-1)
70+
req = requests.get(url)
71+
return req.json()
8372

8473

8574
def fail(msg):
86-
print msg
75+
print(msg)
8776
clean_up()
8877
sys.exit(-1)
8978

9079

9180
def run_cmd(cmd):
81+
if isinstance(cmd, six.string_types):
82+
cmd = cmd.split(' ')
83+
9284
try:
93-
if isinstance(cmd, list):
94-
return subprocess.check_output(cmd)
95-
else:
96-
return subprocess.check_output(cmd.split(" "))
85+
output = subprocess.check_output(cmd)
9786
except subprocess.CalledProcessError as e:
9887
# this avoids hiding the stdout / stderr of failed processes
99-
print 'Command failed: %s' % cmd
100-
print 'With output:'
101-
print '--------------'
102-
print e.output
103-
print '--------------'
88+
print('Command failed: %s' % cmd)
89+
print('With output:')
90+
print('--------------')
91+
print(e.output)
92+
print('--------------')
10493
raise e
10594

95+
if isinstance(output, six.binary_type):
96+
output = output.decode('utf-8')
97+
return output
98+
99+
106100
def continue_maybe(prompt):
107-
result = raw_input("\n%s (y/n): " % prompt)
101+
result = input("\n%s (y/n): " % prompt)
108102
if result.lower() != "y":
109103
fail("Okay, exiting")
110104

@@ -113,46 +107,52 @@ def continue_maybe(prompt):
113107

114108

115109
def clean_up():
116-
print "Restoring head pointer to %s" % original_head
110+
print("Restoring head pointer to %s" % original_head)
117111
run_cmd("git checkout %s" % original_head)
118112

119113
branches = run_cmd("git branch").replace(" ", "").split("\n")
120114

121-
for branch in filter(lambda x: x.startswith(BRANCH_PREFIX), branches):
122-
print "Deleting local branch %s" % branch
115+
for branch in [x for x in branches if x.startswith(BRANCH_PREFIX)]:
116+
print("Deleting local branch %s" % branch)
123117
run_cmd("git branch -D %s" % branch)
124118

125119

126120
# merge the requested PR and return the merge hash
127121
def merge_pr(pr_num, target_ref):
128122
pr_branch_name = "%s_MERGE_PR_%s" % (BRANCH_PREFIX, pr_num)
129-
target_branch_name = "%s_MERGE_PR_%s_%s" % (BRANCH_PREFIX, pr_num, target_ref.upper())
130-
run_cmd("git fetch %s pull/%s/head:%s" % (PR_REMOTE_NAME, pr_num, pr_branch_name))
131-
run_cmd("git fetch %s %s:%s" % (PUSH_REMOTE_NAME, target_ref, target_branch_name))
123+
target_branch_name = "%s_MERGE_PR_%s_%s" % (BRANCH_PREFIX, pr_num,
124+
target_ref.upper())
125+
run_cmd("git fetch %s pull/%s/head:%s" % (PR_REMOTE_NAME, pr_num,
126+
pr_branch_name))
127+
run_cmd("git fetch %s %s:%s" % (PUSH_REMOTE_NAME, target_ref,
128+
target_branch_name))
132129
run_cmd("git checkout %s" % target_branch_name)
133130

134131
had_conflicts = False
135132
try:
136133
run_cmd(['git', 'merge', pr_branch_name, '--squash'])
137134
except Exception as e:
138-
msg = "Error merging: %s\nWould you like to manually fix-up this merge?" % e
135+
msg = ("Error merging: %s\nWould you like to "
136+
"manually fix-up this merge?" % e)
139137
continue_maybe(msg)
140-
msg = "Okay, please fix any conflicts and 'git add' conflicting files... Finished?"
138+
msg = ("Okay, please fix any conflicts and 'git add' "
139+
"conflicting files... Finished?")
141140
continue_maybe(msg)
142141
had_conflicts = True
143142

144143
commit_authors = run_cmd(['git', 'log', 'HEAD..%s' % pr_branch_name,
145144
'--pretty=format:%an <%ae>']).split("\n")
146145
distinct_authors = sorted(set(commit_authors),
147-
key=lambda x: commit_authors.count(x), reverse=True)
146+
key=lambda x: commit_authors.count(x),
147+
reverse=True)
148148
primary_author = distinct_authors[0]
149149
commits = run_cmd(['git', 'log', 'HEAD..%s' % pr_branch_name,
150150
'--pretty=format:%h [%an] %s']).split("\n\n")
151151

152152
merge_message_flags = []
153153

154154
merge_message_flags += ["-m", title]
155-
if body != None:
155+
if body is not None:
156156
merge_message_flags += ["-m", body]
157157

158158
authors = "\n".join(["Author: %s" % a for a in distinct_authors])
@@ -162,14 +162,17 @@ def merge_pr(pr_num, target_ref):
162162
if had_conflicts:
163163
committer_name = run_cmd("git config --get user.name").strip()
164164
committer_email = run_cmd("git config --get user.email").strip()
165-
message = "This patch had conflicts when merged, resolved by\nCommitter: %s <%s>" % (
166-
committer_name, committer_email)
165+
message = ("This patch had conflicts when merged, "
166+
"resolved by\nCommitter: %s <%s>" %
167+
(committer_name, committer_email))
167168
merge_message_flags += ["-m", message]
168169

169-
# The string "Closes #%s" string is required for GitHub to correctly close the PR
170+
# The string "Closes #%s" string is required for GitHub to correctly close
171+
# the PR
170172
merge_message_flags += [
171173
"-m",
172-
"Closes #%s from %s and squashes the following commits:" % (pr_num, pr_repo_desc)]
174+
"Closes #%s from %s and squashes the following commits:"
175+
% (pr_num, pr_repo_desc)]
173176
for c in commits:
174177
merge_message_flags += ["-m", c]
175178

@@ -182,7 +185,8 @@ def merge_pr(pr_num, target_ref):
182185
target_branch_name, PUSH_REMOTE_NAME))
183186

184187
try:
185-
run_cmd('git push %s %s:%s' % (PUSH_REMOTE_NAME, target_branch_name, target_ref))
188+
run_cmd('git push %s %s:%s' % (PUSH_REMOTE_NAME, target_branch_name,
189+
target_ref))
186190
except Exception as e:
187191
clean_up()
188192
fail("Exception while pushing: %s" % e)
@@ -194,65 +198,42 @@ def merge_pr(pr_num, target_ref):
194198
return merge_hash
195199

196200

197-
def cherry_pick(pr_num, merge_hash, default_branch):
198-
pick_ref = raw_input("Enter a branch name [%s]: " % default_branch)
199-
if pick_ref == "":
200-
pick_ref = default_branch
201-
202-
pick_branch_name = "%s_PICK_PR_%s_%s" % (BRANCH_PREFIX, pr_num, pick_ref.upper())
203-
204-
run_cmd("git fetch %s %s:%s" % (PUSH_REMOTE_NAME, pick_ref, pick_branch_name))
205-
run_cmd("git checkout %s" % pick_branch_name)
206-
run_cmd("git cherry-pick -sx %s" % merge_hash)
207-
208-
continue_maybe("Pick complete (local ref %s). Push to %s?" % (
209-
pick_branch_name, PUSH_REMOTE_NAME))
210-
211-
try:
212-
run_cmd('git push %s %s:%s' % (PUSH_REMOTE_NAME, pick_branch_name, pick_ref))
213-
except Exception as e:
214-
clean_up()
215-
fail("Exception while pushing: %s" % e)
216-
217-
pick_hash = run_cmd("git rev-parse %s" % pick_branch_name)[:8]
218-
clean_up()
219-
220-
print("Pull request #%s picked into %s!" % (pr_num, pick_ref))
221-
print("Pick hash: %s" % pick_hash)
222-
return pick_ref
223-
224-
225201
def fix_version_from_branch(branch, versions):
226-
# Note: Assumes this is a sorted (newest->oldest) list of un-released versions
202+
# Note: Assumes this is a sorted (newest->oldest) list of un-released
203+
# versions
227204
if branch == "master":
228205
return versions[0]
229206
else:
230207
branch_ver = branch.replace("branch-", "")
231-
return filter(lambda x: x.name.startswith(branch_ver), versions)[-1]
208+
return [x for x in versions if x.name.startswith(branch_ver)][-1]
209+
232210

233211
def exctract_jira_id(title):
234212
m = re.search(r'^(ARROW-[0-9]+)\b.*$', title)
235213
if m and m.groups > 0:
236214
return m.group(1)
237215
else:
238-
fail("PR title should be prefixed by a jira id \"ARROW-XXX: ...\", found: \"%s\"" % title)
216+
fail("PR title should be prefixed by a jira id "
217+
"\"ARROW-XXX: ...\", found: \"%s\"" % title)
218+
239219

240220
def check_jira(title):
241221
jira_id = exctract_jira_id(title)
242222
asf_jira = jira.client.JIRA({'server': JIRA_API_BASE},
243223
basic_auth=(JIRA_USERNAME, JIRA_PASSWORD))
244224
try:
245-
issue = asf_jira.issue(jira_id)
225+
asf_jira.issue(jira_id)
246226
except Exception as e:
247227
fail("ASF JIRA could not find %s\n%s" % (jira_id, e))
248228

229+
249230
def resolve_jira(title, merge_branches, comment):
250231
asf_jira = jira.client.JIRA({'server': JIRA_API_BASE},
251232
basic_auth=(JIRA_USERNAME, JIRA_PASSWORD))
252233

253234
default_jira_id = exctract_jira_id(title)
254235

255-
jira_id = raw_input("Enter a JIRA id [%s]: " % default_jira_id)
236+
jira_id = input("Enter a JIRA id [%s]: " % default_jira_id)
256237
if jira_id == "":
257238
jira_id = default_jira_id
258239

@@ -271,30 +252,33 @@ def resolve_jira(title, merge_branches, comment):
271252

272253
if cur_status == "Resolved" or cur_status == "Closed":
273254
fail("JIRA issue %s already has status '%s'" % (jira_id, cur_status))
274-
print ("=== JIRA %s ===" % jira_id)
275-
print ("summary\t\t%s\nassignee\t%s\nstatus\t\t%s\nurl\t\t%s/%s\n" % (
276-
cur_summary, cur_assignee, cur_status, JIRA_BASE, jira_id))
255+
print("=== JIRA %s ===" % jira_id)
256+
print("summary\t\t%s\nassignee\t%s\nstatus\t\t%s\nurl\t\t%s/%sf\n"
257+
% (cur_summary, cur_assignee, cur_status, JIRA_BASE, jira_id))
277258

278259
resolve = filter(lambda a: a['name'] == "Resolve Issue",
279260
asf_jira.transitions(jira_id))[0]
280261
asf_jira.transition_issue(jira_id, resolve["id"], comment=comment)
281262

282-
print "Succesfully resolved %s!" % (jira_id)
263+
print("Succesfully resolved %s!" % (jira_id))
283264

284265

285266
if not JIRA_USERNAME:
286-
JIRA_USERNAME = raw_input("Env JIRA_USERNAME not set, please enter your JIRA username:")
267+
JIRA_USERNAME = input("Env JIRA_USERNAME not set, "
268+
"please enter your JIRA username:")
287269

288270
if not JIRA_PASSWORD:
289-
JIRA_PASSWORD = getpass.getpass("Env JIRA_PASSWORD not set, please enter your JIRA password:")
271+
JIRA_PASSWORD = getpass.getpass("Env JIRA_PASSWORD not set, please enter "
272+
"your JIRA password:")
290273

291274
branches = get_json("%s/branches" % GITHUB_API_BASE)
292-
branch_names = filter(lambda x: x.startswith("branch-"), [x['name'] for x in branches])
275+
branch_names = [x['name'] for x in branches if x['name'].startswith('branch-')]
276+
293277
# Assumes branch names can be sorted lexicographically
294278
# Julien: I commented this out as we don't have any "branch-*" branch yet
295-
#latest_branch = sorted(branch_names, reverse=True)[0]
279+
# latest_branch = sorted(branch_names, reverse=True)[0]
296280

297-
pr_num = raw_input("Which pull request would you like to merge? (e.g. 34): ")
281+
pr_num = input("Which pull request would you like to merge? (e.g. 34): ")
298282
pr = get_json("%s/pulls/%s" % (GITHUB_API_BASE, pr_num))
299283

300284
url = pr["url"]
@@ -307,42 +291,41 @@ def resolve_jira(title, merge_branches, comment):
307291
pr_repo_desc = "%s/%s" % (user_login, base_ref)
308292

309293
if pr["merged"] is True:
310-
print "Pull request %s has already been merged, assuming you want to backport" % pr_num
294+
print("Pull request %s has already been merged, "
295+
"assuming you want to backport" % pr_num)
311296
merge_commit_desc = run_cmd([
312297
'git', 'log', '--merges', '--first-parent',
313298
'--grep=pull request #%s' % pr_num, '--oneline']).split("\n")[0]
314299
if merge_commit_desc == "":
315-
fail("Couldn't find any merge commit for #%s, you may need to update HEAD." % pr_num)
300+
fail("Couldn't find any merge commit for #%s, "
301+
"you may need to update HEAD." % pr_num)
316302

317303
merge_hash = merge_commit_desc[:7]
318304
message = merge_commit_desc[8:]
319305

320-
print "Found: %s" % message
321-
maybe_cherry_pick(pr_num, merge_hash, latest_branch)
306+
print("Found: %s" % message)
322307
sys.exit(0)
323308

324309
if not bool(pr["mergeable"]):
325-
msg = "Pull request %s is not mergeable in its current form.\n" % pr_num + \
326-
"Continue? (experts only!)"
310+
msg = ("Pull request %s is not mergeable in its current form.\n"
311+
% pr_num + "Continue? (experts only!)")
327312
continue_maybe(msg)
328313

329-
print ("\n=== Pull Request #%s ===" % pr_num)
330-
print ("title\t%s\nsource\t%s\ntarget\t%s\nurl\t%s" % (
331-
title, pr_repo_desc, target_ref, url))
314+
print("\n=== Pull Request #%s ===" % pr_num)
315+
print("title\t%s\nsource\t%s\ntarget\t%s\nurl\t%s"
316+
% (title, pr_repo_desc, target_ref, url))
332317
continue_maybe("Proceed with merging pull request #%s?" % pr_num)
333318

334319
merged_refs = [target_ref]
335320

336321
merge_hash = merge_pr(pr_num, target_ref)
337322

338-
pick_prompt = "Would you like to pick %s into another branch?" % merge_hash
339-
while raw_input("\n%s (y/n): " % pick_prompt).lower() == "y":
340-
merged_refs = merged_refs + [cherry_pick(pr_num, merge_hash, latest_branch)]
341-
342323
if JIRA_IMPORTED:
343324
continue_maybe("Would you like to update the associated JIRA?")
344-
jira_comment = "Issue resolved by pull request %s\n[%s/%s]" % (pr_num, GITHUB_BASE, pr_num)
325+
jira_comment = ("Issue resolved by pull request %s\n[%s/%s]"
326+
% (pr_num, GITHUB_BASE, pr_num))
345327
resolve_jira(title, merged_refs, jira_comment)
346328
else:
347-
print "Could not find jira-python library. Run 'sudo pip install jira-python' to install."
348-
print "Exiting without trying to close the associated JIRA."
329+
print("Could not find jira-python library. "
330+
"Run 'sudo pip install jira-python' to install.")
331+
print("Exiting without trying to close the associated JIRA.")

0 commit comments

Comments
 (0)