Skip to content

Commit 7a9a51d

Browse files
authored
[FEAT][INFRA] Improve Merge PR Script
### What changes were proposed in this pull request? This patch improves the merge PR script by collecting all authors and the committer who signed of via the script and then prepare the commit message accordingly. Lastly it will squash merge the PR via the Github API and the updated Title and Commit message instead of manually pushing the PR. ### Why are the changes needed? Better GH integration ### Does this PR introduce _any_ user-facing change? No ### How was this patch tested? Manually using the PRs: #157 #156 #159 and #160. Closes #161 from grundprinzip/improve_infra. Authored-by: Martin Grund <martin.grund@databricks.com> Signed-off-by: Martin Grund <martin.grund@databricks.com>
1 parent 984635a commit 7a9a51d

File tree

1 file changed

+73
-38
lines changed

1 file changed

+73
-38
lines changed

merge_connect_go_pr.py

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import subprocess
3232
import sys
3333
import traceback
34+
import requests
3435
from urllib.request import urlopen
3536
from urllib.request import Request
3637
from urllib.error import HTTPError
@@ -89,6 +90,24 @@ def get_json(url):
8990
sys.exit(-1)
9091

9192

93+
def get_pull_request(pr_num):
94+
headers = {
95+
"Authorization": f"token {GITHUB_OAUTH_KEY}",
96+
"Accept": "application/vnd.github.v3+json",
97+
}
98+
response = requests.get(
99+
f"{GITHUB_API_BASE}/pulls/{pr_num}",
100+
headers=headers,
101+
)
102+
if response.status_code == 200:
103+
return response.json()
104+
else:
105+
error_message = (f"Failed to get pull request #{pr_num}. "
106+
f"Status code: {response.status_code}")
107+
error_message += f"\nResponse: {response.text}"
108+
fail(error_message)
109+
110+
92111
def fail(msg):
93112
print(msg)
94113
clean_up()
@@ -129,19 +148,26 @@ def merge_pr(pr_num, target_ref, title, body, pr_repo_desc):
129148
run_cmd("git fetch %s %s:%s" % (PUSH_REMOTE_NAME, target_ref, target_branch_name))
130149
run_cmd("git checkout %s" % target_branch_name)
131150

132-
had_conflicts = False
133-
try:
134-
run_cmd(["git", "merge", pr_branch_name, "--squash"])
135-
except Exception as e:
136-
msg = "Error merging: %s\nWould you like to manually fix-up this merge?" % e
137-
continue_maybe(msg)
138-
msg = "Okay, please fix any conflicts and 'git add' conflicting files... Finished?"
139-
continue_maybe(msg)
140-
had_conflicts = True
151+
# Get all the data from the pull request.
152+
pr = get_pull_request(pr_num)
153+
154+
# Check if the PR is mergeable and still open:
155+
if not pr["mergeable"]:
156+
fail(f"Pull request #{pr_num} is not mergeable in its current form.")
157+
158+
# Check if the PR is still open.
159+
if pr["state"] != "open":
160+
fail(f"Pull request #{pr_num} is not open.")
161+
162+
if pr["merged"]:
163+
fail(f"Pull request #{pr_num} has already been merged.")
164+
165+
if pr["draft"]:
166+
fail(f"Pull request #{pr_num} is a draft.")
141167

142168
# First commit author should be considered as the primary author when the rank is the same
143169
commit_authors = run_cmd(
144-
["git", "log", "HEAD..%s" % pr_branch_name, "--pretty=format:%an <%ae>", "--reverse"]
170+
["git", "log", "%s..%s" % (target_branch_name, pr_branch_name), "--pretty=format:%an <%ae>", "--reverse"]
145171
).split("\n")
146172
distinct_authors = sorted(
147173
list(dict.fromkeys(commit_authors)), key=lambda x: commit_authors.count(x), reverse=True
@@ -157,52 +183,61 @@ def merge_pr(pr_num, target_ref, title, body, pr_repo_desc):
157183
distinct_authors = list(filter(lambda x: x != primary_author, distinct_authors))
158184
distinct_authors.insert(0, primary_author)
159185

160-
merge_message_flags = []
161-
162-
merge_message_flags += ["-m", title]
186+
merge_message = ""
163187
if body is not None:
164188
# We remove @ symbols from the body to avoid triggering e-mails
165189
# to people every time someone creates a public fork of Spark.
166-
merge_message_flags += ["-m", body.replace("@", "")]
190+
merge_message += body.replace("@", "")
167191

168192
committer_name = run_cmd("git config --get user.name").strip()
169193
committer_email = run_cmd("git config --get user.email").strip()
170194

171-
if had_conflicts:
172-
message = "This patch had conflicts when merged, resolved by\nCommitter: %s <%s>" % (
173-
committer_name,
174-
committer_email,
175-
)
176-
merge_message_flags += ["-m", message]
177-
178195
# The string "Closes #%s" string is required for GitHub to correctly close the PR
179-
merge_message_flags += ["-m", "Closes #%s from %s." % (pr_num, pr_repo_desc)]
196+
merge_message += "\n\n"
197+
merge_message += "Closes #%s from %s." % (pr_num, pr_repo_desc)
180198

181199
authors = "Authored-by:" if len(distinct_authors) == 1 else "Lead-authored-by:"
182200
authors += " %s" % (distinct_authors.pop(0))
183201
if len(distinct_authors) > 0:
184202
authors += "\n" + "\n".join(["Co-authored-by: %s" % a for a in distinct_authors])
185203
authors += "\n" + "Signed-off-by: %s <%s>" % (committer_name, committer_email)
186204

187-
merge_message_flags += ["-m", authors]
188-
189-
run_cmd(["git", "commit", '--author="%s"' % primary_author] + merge_message_flags)
190-
191-
continue_maybe(
192-
"Merge complete (local ref %s). Push to %s?" % (target_branch_name, PUSH_REMOTE_NAME)
205+
merge_message += "\n\n"
206+
merge_message += authors
207+
208+
# Merge the Pull Request using the commit message and title and squash it.
209+
headers = {
210+
"Authorization": f"token {GITHUB_OAUTH_KEY}",
211+
"Accept": "application/vnd.github.v3+json",
212+
}
213+
214+
data = {
215+
"commit_title": title,
216+
"commit_message": merge_message,
217+
# Must be squash, always.
218+
"merge_method": "squash",
219+
}
220+
221+
continue_maybe("Collected all data. Ready to merge PR?")
222+
223+
# Run the request to merge the PR.
224+
response = requests.put(
225+
f"{GITHUB_API_BASE}/pulls/{pr_num}/merge",
226+
headers=headers,
227+
json=data
193228
)
194229

195-
try:
196-
run_cmd("git push %s %s:%s" % (PUSH_REMOTE_NAME, target_branch_name, target_ref))
197-
except Exception as e:
230+
if response.status_code == 200:
231+
merge_response_json = response.json()
232+
merge_commit_sha = merge_response_json.get("sha")
233+
print(f"Pull request #{pr_num} merged. Sha: #{merge_commit_sha}")
198234
clean_up()
199-
fail("Exception while pushing: %s" % e)
200-
201-
merge_hash = run_cmd("git rev-parse %s" % target_branch_name)[:8]
202-
clean_up()
203-
print("Pull request #%s merged!" % pr_num)
204-
print("Merge hash: %s" % merge_hash)
205-
return merge_hash
235+
return merge_commit_sha
236+
else:
237+
error_message = f"Failed to merge pull request #{pr_num}. Status code: {response.status_code}"
238+
error_message += f"\nResponse: {response.text}"
239+
clean_up()
240+
fail(error_message)
206241

207242

208243
def cherry_pick(pr_num, merge_hash, default_branch):

0 commit comments

Comments
 (0)