Skip to content

Commit 0e01e45

Browse files
committed
devtools: Fetch and display ACKs at sign-off time in github-merge
- Fetch the ACKs only at sign-off time. This makes sure that any last-minute ACKs are included (fixes #16200) - Show a list of ACKs and their author before signing off, and warn if there are none
1 parent f6f9248 commit 0e01e45

File tree

1 file changed

+49
-19
lines changed

1 file changed

+49
-19
lines changed

contrib/devtools/github-merge.py

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@
3232
# OS specific configuration for terminal attributes
3333
ATTR_RESET = ''
3434
ATTR_PR = ''
35+
ATTR_NAME = ''
36+
ATTR_WARN = ''
3537
COMMIT_FORMAT = '%H %s (%an)%d'
3638
if os.name == 'posix': # if posix, assume we can use basic terminal escapes
3739
ATTR_RESET = '\033[0m'
3840
ATTR_PR = '\033[1;36m'
41+
ATTR_NAME = '\033[0;36m'
42+
ATTR_WARN = '\033[1;31m'
3943
COMMIT_FORMAT = '%C(bold blue)%H%Creset %s %C(cyan)(%an)%Creset%C(green)%d%Creset'
4044

4145
def git_config_get(option, default=None):
@@ -164,18 +168,36 @@ def tree_sha512sum(commit='HEAD'):
164168
return overall.hexdigest()
165169

166170
def get_acks_from_comments(head_commit, comments):
167-
assert len(head_commit) == 6
168-
ack_str ='\n\nACKs for commit {}:\n'.format(head_commit)
171+
# Look for abbreviated commit id, because not everyone wants to type/paste
172+
# the whole thing and the chance of collisions within a PR is small enough
173+
head_abbrev = head_commit[0:6]
174+
acks = []
169175
for c in comments:
170-
review = [l for l in c['body'].split('\r\n') if 'ACK' in l and head_commit in l]
176+
review = [l for l in c['body'].split('\r\n') if 'ACK' in l and head_abbrev in l]
171177
if review:
172-
ack_str += ' {}:\n'.format(c['user']['login'])
173-
ack_str += ' {}\n'.format(review[0])
178+
acks.append((c['user']['login'], review[0]))
179+
return acks
180+
181+
def make_acks_message(head_commit, acks):
182+
if acks:
183+
ack_str ='\n\nACKs for top commit:\n'.format(head_commit)
184+
for name, msg in acks:
185+
ack_str += ' {}:\n'.format(name)
186+
ack_str += ' {}\n'.format(msg)
187+
else:
188+
ack_str ='\n\nTop commit has no ACKs.\n'
174189
return ack_str
175190

176-
def print_merge_details(pull, title, branch, base_branch, head_branch):
191+
def print_merge_details(pull, title, branch, base_branch, head_branch, acks):
177192
print('%s#%s%s %s %sinto %s%s' % (ATTR_RESET+ATTR_PR,pull,ATTR_RESET,title,ATTR_RESET+ATTR_PR,branch,ATTR_RESET))
178193
subprocess.check_call([GIT,'log','--graph','--topo-order','--pretty=format:'+COMMIT_FORMAT,base_branch+'..'+head_branch])
194+
if acks is not None:
195+
if acks:
196+
print('{}ACKs:{}'.format(ATTR_PR, ATTR_RESET))
197+
for (name, message) in acks:
198+
print('* {} {}({}){}'.format(message, ATTR_NAME, name, ATTR_RESET))
199+
else:
200+
print('{}Top commit has no ACKs!{}'.format(ATTR_WARN, ATTR_RESET))
179201

180202
def parse_arguments():
181203
epilog = '''
@@ -225,9 +247,6 @@ def main():
225247
info = retrieve_pr_info(repo,pull,ghtoken)
226248
if info is None:
227249
sys.exit(1)
228-
comments = retrieve_pr_comments(repo,pull,ghtoken) + retrieve_pr_reviews(repo,pull,ghtoken)
229-
if comments is None:
230-
sys.exit(1)
231250
title = info['title'].strip()
232251
body = info['body'].strip()
233252
# precedence order for destination branch argument:
@@ -257,6 +276,8 @@ def main():
257276
sys.exit(3)
258277
try:
259278
subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+head_branch], stdout=devnull, stderr=stdout)
279+
head_commit = subprocess.check_output([GIT,'log','-1','--pretty=format:%H',head_branch]).decode('utf-8')
280+
assert len(head_commit) == 40
260281
except subprocess.CalledProcessError:
261282
print("ERROR: Cannot find head of pull request #%s on %s." % (pull,host_repo), file=stderr)
262283
sys.exit(3)
@@ -281,7 +302,6 @@ def main():
281302
message = firstline + '\n\n'
282303
message += subprocess.check_output([GIT,'log','--no-merges','--topo-order','--pretty=format:%H %s (%an)',base_branch+'..'+head_branch]).decode('utf-8')
283304
message += '\n\nPull request description:\n\n ' + body.replace('\n', '\n ') + '\n'
284-
message += get_acks_from_comments(head_commit=subprocess.check_output([GIT,'log','-1','--pretty=format:%H',head_branch]).decode('utf-8')[:6], comments=comments)
285305
try:
286306
subprocess.check_call([GIT,'merge','-q','--commit','--no-edit','--no-ff','--no-gpg-sign','-m',message.encode('utf-8'),head_branch])
287307
except subprocess.CalledProcessError:
@@ -299,20 +319,14 @@ def main():
299319
if len(symlink_files) > 0:
300320
sys.exit(4)
301321

302-
# Put tree SHA512 into the message
322+
# Compute SHA512 of git tree (to be able to detect changes before sign-off)
303323
try:
304324
first_sha512 = tree_sha512sum()
305-
message += '\n\nTree-SHA512: ' + first_sha512
306325
except subprocess.CalledProcessError:
307326
print("ERROR: Unable to compute tree hash")
308327
sys.exit(4)
309-
try:
310-
subprocess.check_call([GIT,'commit','--amend','--no-gpg-sign','-m',message.encode('utf-8')])
311-
except subprocess.CalledProcessError:
312-
print("ERROR: Cannot update message.", file=stderr)
313-
sys.exit(4)
314328

315-
print_merge_details(pull, title, branch, base_branch, head_branch)
329+
print_merge_details(pull, title, branch, base_branch, head_branch, None)
316330
print()
317331

318332
# Run test command if configured.
@@ -345,8 +359,24 @@ def main():
345359
print("ERROR: Tree hash changed unexpectedly",file=stderr)
346360
sys.exit(8)
347361

362+
# Retrieve PR comments and ACKs and add to commit message, store ACKs to print them with commit
363+
# description
364+
comments = retrieve_pr_comments(repo,pull,ghtoken) + retrieve_pr_reviews(repo,pull,ghtoken)
365+
if comments is None:
366+
print("ERROR: Could not fetch PR comments and reviews",file=stderr)
367+
sys.exit(1)
368+
acks = get_acks_from_comments(head_commit=head_commit, comments=comments)
369+
message += make_acks_message(head_commit=head_commit, acks=acks)
370+
# end message with SHA512 tree hash, then update message
371+
message += '\n\nTree-SHA512: ' + first_sha512
372+
try:
373+
subprocess.check_call([GIT,'commit','--amend','--no-gpg-sign','-m',message.encode('utf-8')])
374+
except subprocess.CalledProcessError:
375+
print("ERROR: Cannot update message.", file=stderr)
376+
sys.exit(4)
377+
348378
# Sign the merge commit.
349-
print_merge_details(pull, title, branch, base_branch, head_branch)
379+
print_merge_details(pull, title, branch, base_branch, head_branch, acks)
350380
while True:
351381
reply = ask_prompt("Type 's' to sign off on the above merge, or 'x' to reject and exit.").lower()
352382
if reply == 's':

0 commit comments

Comments
 (0)