26
26
27
27
import json
28
28
import os
29
+ import re
29
30
import subprocess
30
31
import sys
31
32
import tempfile
32
33
import urllib2
33
34
35
+ try :
36
+ import jira .client
37
+ JIRA_IMPORTED = True
38
+ except ImportError :
39
+ JIRA_IMPORTED = False
40
+
34
41
# Location of your Spark git development area
35
42
SPARK_HOME = os .environ .get ("SPARK_HOME" , "/home/patrick/Documents/spark" )
36
43
# Remote name which points to the Gihub site
37
44
PR_REMOTE_NAME = os .environ .get ("PR_REMOTE_NAME" , "apache-github" )
38
45
# Remote name which points to Apache git
39
46
PUSH_REMOTE_NAME = os .environ .get ("PUSH_REMOTE_NAME" , "apache" )
40
-
41
- GIT_API_BASE = "https://api.github.com/repos/apache/spark"
47
+ # ASF JIRA username
48
+ JIRA_USERNAME = os .environ .get ("JIRA_USERNAME" , "pwendell" )
49
+ # ASF JIRA password
50
+ JIRA_PASSWORD = os .environ .get ("JIRA_PASSWORD" , "1234" )
51
+
52
+ GITHUB_BASE = "https://github.com/apache/spark/pull"
53
+ GITHUB_API_BASE = "https://api.github.com/repos/apache/spark"
54
+ JIRA_BASE = "https://issues.apache.org/jira/browse"
55
+ JIRA_API_BASE = "https://issues.apache.org/jira"
42
56
# Prefix added to temporary branches
43
57
BRANCH_PREFIX = "PR_TOOL"
44
58
@@ -145,8 +159,7 @@ def merge_pr(pr_num, target_ref):
145
159
return merge_hash
146
160
147
161
148
- def maybe_cherry_pick (pr_num , merge_hash , default_branch ):
149
- continue_maybe ("Would you like to pick %s into another branch?" % merge_hash )
162
+ def cherry_pick (pr_num , merge_hash , default_branch ):
150
163
pick_ref = raw_input ("Enter a branch name [%s]: " % default_branch )
151
164
if pick_ref == "" :
152
165
pick_ref = default_branch
@@ -171,14 +184,86 @@ def maybe_cherry_pick(pr_num, merge_hash, default_branch):
171
184
172
185
print ("Pull request #%s picked into %s!" % (pr_num , pick_ref ))
173
186
print ("Pick hash: %s" % pick_hash )
187
+ return pick_ref
188
+
189
+ def fix_version_from_branch (branch , versions ):
190
+ # Note: Assumes this is a sorted (newest->oldest) list of un-released versions
191
+ if branch == "master" :
192
+ return versions [0 ]
193
+ else :
194
+ branch_ver = branch .replace ("branch-" , "" )
195
+ return filter (lambda x : x .name .startswith (branch_ver ), versions )[- 1 ]
196
+
197
+ def resolve_jira (title , merge_branches , comment ):
198
+ asf_jira = jira .client .JIRA ({'server' : JIRA_API_BASE },
199
+ basic_auth = (JIRA_USERNAME , JIRA_PASSWORD ))
200
+
201
+ default_jira_id = ""
202
+ search = re .findall ("SPARK-[0-9]{4,5}" , title )
203
+ if len (search ) > 0 :
204
+ default_jira_id = search [0 ]
205
+
206
+ jira_id = raw_input ("Enter a JIRA id [%s]: " % default_jira_id )
207
+ if jira_id == "" :
208
+ jira_id = default_jira_id
174
209
175
- branches = get_json ("%s/branches" % GIT_API_BASE )
210
+ try :
211
+ issue = asf_jira .issue (jira_id )
212
+ except Exception as e :
213
+ fail ("ASF JIRA could not find %s\n %s" % (jira_id , e ))
214
+
215
+ cur_status = issue .fields .status .name
216
+ cur_summary = issue .fields .summary
217
+ cur_assignee = issue .fields .assignee
218
+ if cur_assignee == None :
219
+ cur_assignee = "NOT ASSIGNED!!!"
220
+ else :
221
+ cur_assignee = cur_assignee .displayName
222
+
223
+ if cur_status == "Resolved" or cur_status == "Closed" :
224
+ fail ("JIRA issue %s already has status '%s'" % (jira_id , cur_status ))
225
+ print ("=== JIRA %s ===" % jira_id )
226
+ print ("summary\t \t %s\n assignee\t %s\n status\t \t %s\n url\t \t %s/%s\n " % (
227
+ cur_summary , cur_assignee , cur_status , JIRA_BASE , jira_id ))
228
+
229
+ versions = asf_jira .project_versions ("SPARK" )
230
+ versions = sorted (versions , key = lambda x : x .name , reverse = True )
231
+ versions = filter (lambda x : x .raw ['released' ] == False , versions )
232
+
233
+ default_fix_versions = map (lambda x : fix_version_from_branch (x , versions ).name , merge_branches )
234
+ for v in default_fix_versions :
235
+ # Handles the case where we have forked a release branch but not yet made the release.
236
+ # In this case, if the PR is committed to the master branch and the release branch, we
237
+ # only consider the release branch to be the fix version. E.g. it is not valid to have
238
+ # both 1.1.0 and 1.0.0 as fix versions.
239
+ (major , minor , patch ) = v .split ("." )
240
+ if patch == 0 :
241
+ previous = "%s.%s.%s" % (major , int (minor ) - 1 , 0 )
242
+ if previous in default_fix_versions :
243
+ default_fix_versions = filter (lambda x : x != v , default_fix_versions )
244
+ default_fix_versions = "," .join (default_fix_versions )
245
+
246
+ fix_versions = raw_input ("Enter comma-separated fix version(s) [%s]: " % default_fix_versions )
247
+ if fix_versions == "" :
248
+ fix_versions = default_fix_versions
249
+ fix_versions = fix_versions .replace (" " , "" ).split ("," )
250
+
251
+ def get_version_json (version_str ):
252
+ return filter (lambda v : v .name == version_str , versions )[0 ].raw
253
+ jira_fix_versions = map (lambda v : get_version_json (v ), fix_versions )
254
+
255
+ resolve = filter (lambda a : a ['name' ] == "Resolve Issue" , asf_jira .transitions (jira_id ))[0 ]
256
+ asf_jira .transition_issue (jira_id , resolve ["id" ], fixVersions = jira_fix_versions , comment = comment )
257
+
258
+ print "Succesfully resolved %s with fixVersions=%s!" % (jira_id , fix_versions )
259
+
260
+ branches = get_json ("%s/branches" % GITHUB_API_BASE )
176
261
branch_names = filter (lambda x : x .startswith ("branch-" ), [x ['name' ] for x in branches ])
177
262
# Assumes branch names can be sorted lexicographically
178
263
latest_branch = sorted (branch_names , reverse = True )[0 ]
179
264
180
265
pr_num = raw_input ("Which pull request would you like to merge? (e.g. 34): " )
181
- pr = get_json ("%s/pulls/%s" % (GIT_API_BASE , pr_num ))
266
+ pr = get_json ("%s/pulls/%s" % (GITHUB_API_BASE , pr_num ))
182
267
183
268
url = pr ["url" ]
184
269
title = pr ["title" ]
@@ -208,11 +293,22 @@ def maybe_cherry_pick(pr_num, merge_hash, default_branch):
208
293
continue_maybe (msg )
209
294
210
295
print ("\n === Pull Request #%s ===" % pr_num )
211
- print ("title\t %s\n source\t %s\n target\t %s\n url\t %s" % (
296
+ print ("title\t %s\n source\t %s\n target\t %s\n url\t %s" % (
212
297
title , pr_repo_desc , target_ref , url ))
213
298
continue_maybe ("Proceed with merging pull request #%s?" % pr_num )
214
299
300
+ merged_refs = [target_ref ]
301
+
215
302
merge_hash = merge_pr (pr_num , target_ref )
216
303
217
- while True :
218
- maybe_cherry_pick (pr_num , merge_hash , latest_branch )
304
+ pick_prompt = "Would you like to pick %s into another branch?" % merge_hash
305
+ while raw_input ("\n %s (y/n): " % pick_prompt ).lower () == "y" :
306
+ merged_refs = merged_refs + [cherry_pick (pr_num , merge_hash , latest_branch )]
307
+
308
+ if JIRA_IMPORTED :
309
+ continue_maybe ("Would you like to update an associated JIRA?" )
310
+ jira_comment = "Issue resolved by pull request %s\n [%s/%s]" % (pr_num , GITHUB_BASE , pr_num )
311
+ resolve_jira (title , merged_refs , jira_comment )
312
+ else :
313
+ print "Could not find jira-python library. Run 'sudo pip install jira-python' to install."
314
+ print "Exiting without trying to close the associated JIRA."
0 commit comments