Skip to content

Commit 3f8e86a

Browse files
authored
KAFKA-19013 Reformat PR body to 72 characters (#19242)
Parse the body of a PR and re-format it to a text wrap of 72 characters. Since GitHub will do this automatically when merging PRs with the merge queue, we should do our best to pre-format the commit message. Reviewers: Chia-Ping Tsai <[email protected]>
1 parent 8c77953 commit 3f8e86a

File tree

1 file changed

+54
-7
lines changed

1 file changed

+54
-7
lines changed

.github/scripts/pr-format.py

+54-7
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
import shlex
2323
import sys
2424
import tempfile
25+
import textwrap
2526
from typing import Dict, Optional, TextIO
2627

27-
2828
logger = logging.getLogger()
2929
logger.setLevel(logging.DEBUG)
3030
handler = logging.StreamHandler(sys.stderr)
@@ -61,6 +61,7 @@ def write_commit(io: TextIO, title: str, body: str):
6161
io.write(body.encode())
6262
io.flush()
6363

64+
6465
def parse_trailers(title, body) -> Dict:
6566
trailers = defaultdict(list)
6667

@@ -77,6 +78,29 @@ def parse_trailers(title, body) -> Dict:
7778
return trailers
7879

7980

81+
def split_paragraphs(text: str):
82+
"""
83+
Split the given text into a generator of paragraph lines and a boolean "markdown" flag.
84+
85+
If any line of a paragraph starts with a markdown character, we will assume the whole paragraph
86+
contains markdown.
87+
"""
88+
lines = text.splitlines(keepends=True)
89+
paragraph = []
90+
markdown = False
91+
for line in lines:
92+
if line.strip() == "":
93+
if len(paragraph) > 0:
94+
yield paragraph, markdown
95+
paragraph.clear()
96+
markdown = False
97+
else:
98+
if line[0] in ("#", "*", "-", "=") or line[0].isdigit():
99+
markdown = True
100+
paragraph.append(line)
101+
yield paragraph, markdown
102+
103+
80104
if __name__ == "__main__":
81105
"""
82106
This script performs some basic linting of our PR titles and body. The PR number is read from the PR_NUMBER
@@ -96,10 +120,6 @@ def parse_trailers(title, body) -> Dict:
96120
* Has "Reviewers:" trailer if the PR is approved
97121
"""
98122

99-
if not get_env("GITHUB_ACTIONS"):
100-
print("This script is intended to by run by GitHub Actions.")
101-
exit(1)
102-
103123
pr_number = get_env("PR_NUMBER")
104124
cmd = f"gh pr view {pr_number} --json 'title,body,reviews'"
105125
p = subprocess.run(shlex.split(cmd), capture_output=True)
@@ -132,6 +152,32 @@ def check(positive_assertion, ok_msg, err_msg):
132152
check("Delete this text and replace" not in body, "PR template text not present", "PR template text should be removed")
133153
check("Committer Checklist" not in body, "PR template text not present", "Old PR template text should be removed")
134154

155+
paragraph_iter = split_paragraphs(body)
156+
new_paragraphs = []
157+
for p, markdown in paragraph_iter:
158+
if markdown:
159+
# If a paragraph looks like it has markdown in it, wrap each line separately.
160+
new_lines = []
161+
for line in p:
162+
new_lines.append(textwrap.fill(line, width=72, break_long_words=False, break_on_hyphens=False, replace_whitespace=False))
163+
rewrapped_p = "\n".join(new_lines)
164+
else:
165+
rewrapped_p = textwrap.fill("".join(p), width=72, break_long_words=False, break_on_hyphens=False, replace_whitespace=True)
166+
new_paragraphs.append(rewrapped_p + "\n")
167+
body = "\n".join(new_paragraphs)
168+
169+
if get_env("GITHUB_ACTIONS"):
170+
with tempfile.NamedTemporaryFile() as fp:
171+
fp.write(body.encode())
172+
fp.flush()
173+
cmd = f"gh pr edit {pr_number} --body-file {fp.name}"
174+
p = subprocess.run(shlex.split(cmd), capture_output=True)
175+
fp.close()
176+
if p.returncode != 0:
177+
logger.error(f"Could not update PR {pr_number}. STDOUT: {p.stdout.decode()}")
178+
else:
179+
logger.info(f"Not reformatting {pr_number} since this is not running on GitHub Actions.")
180+
135181
# Check for Reviewers
136182
approved = has_approval(reviews)
137183
if approved:
@@ -143,12 +189,13 @@ def check(positive_assertion, ok_msg, err_msg):
143189
logger.debug(reviewer_in_body)
144190

145191
logger.debug("Commit will look like:\n")
146-
logger.debug("```")
192+
logger.debug("<pre>")
147193
io = BytesIO()
194+
title += f" (#{pr_number})"
148195
write_commit(io, title, body)
149196
io.seek(0)
150197
logger.debug(io.read().decode())
151-
logger.debug("```\n")
198+
logger.debug("</pre>\n")
152199

153200
exit_code = 0
154201
logger.debug("Validation results:")

0 commit comments

Comments
 (0)