Skip to content

Commit 41f820e

Browse files
committed
feat(release): auto-add RC tasks to checklist in create-rc
- Add `add_rc_task_to_body` to `release_issue.py` to dynamically insert new `Tag RC<N>` tasks into the checklist before `Tag Final`. - Modify `create_rc.py` to detect if the next RC task is missing from the checklist, and if so, auto-add it and update the tracking issue body before proceeding. - Add `test_create_rc_auto_add_task` to `create_rc_test.py` to verify this behavior.
1 parent e3c8867 commit 41f820e

3 files changed

Lines changed: 82 additions & 13 deletions

File tree

tests/tools/private/release/create_rc_test.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import argparse
12
import os
23
import pathlib
34
import tempfile
@@ -212,6 +213,46 @@ def test_create_rc_with_finished_backports(self):
212213
self.mock_git.tag.assert_called_once_with("2.0.0-rc0", "my-remote/release/2.0")
213214
self.mock_git.push.assert_called_once_with("my-remote", "2.0.0-rc0")
214215

216+
def test_create_rc_auto_add_task(self):
217+
# Arrange
218+
args = argparse.Namespace(issue=123, remote="my-remote")
219+
self.mock_gh.get_issue_title.return_value = "Release 2.0.0"
220+
self.mock_gh.get_issue_body.return_value = """
221+
## Checklist
222+
- [x] Prepare Release | status=done pr=#122 commit=abcdef12
223+
- [x] Create Release branch | status=done branch=release/2.0 commit=abcdef12
224+
- [x] Tag RC0 | status=done tag=2.0.0-rc0 commit=abcdef12
225+
- [ ] Tag Final
226+
"""
227+
self.mock_git.get_remote_tags.return_value = ["2.0.0-rc0"]
228+
self.mock_git.get_commit_sha.return_value = "1234567890"
229+
230+
# Act
231+
result = CreateRc(args, self.mock_git, self.mock_gh).run()
232+
233+
# Assert
234+
self.assertEqual(result, 0)
235+
self.mock_git.tag.assert_called_once_with("2.0.0-rc1", "my-remote/release/2.0")
236+
self.mock_git.push.assert_called_once_with("my-remote", "2.0.0-rc1")
237+
238+
self.assertEqual(self.mock_gh.update_issue_body.call_count, 2)
239+
call1_args = self.mock_gh.update_issue_body.call_args_list[0][0]
240+
call2_args = self.mock_gh.update_issue_body.call_args_list[1][0]
241+
242+
self.assertEqual(call1_args[0], 123)
243+
self.assertIn("- [ ] Tag RC1", call1_args[1])
244+
self.assertIn(
245+
"- [x] Tag RC0 | status=done tag=2.0.0-rc0 commit=abcdef12\n- [ ]"
246+
" Tag RC1\n- [ ] Tag Final",
247+
call1_args[1].strip(),
248+
)
249+
250+
self.assertEqual(call2_args[0], 123)
251+
self.assertIn(
252+
"- [x] Tag RC1 | status=done tag=2.0.0-rc1 commit= 12345678",
253+
call2_args[1],
254+
)
255+
215256

216257
if __name__ == "__main__":
217258
unittest.main()

tools/private/release/create_rc.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from tools.private.release.git import Git
55
from tools.private.release.release_issue import (
66
RELEASE_TITLE_RE,
7+
add_rc_task_to_body,
78
parse_backports,
89
parse_checklist_state,
910
update_task_in_body,
@@ -77,19 +78,17 @@ def run(self) -> int:
7778
# Precheck: next RC number must exist and be unchecked in the checklist
7879
rc_tags = state.get("rc_tags", {})
7980
if next_rc_num not in rc_tags:
80-
print(
81-
f"Error: Checklist is missing required task 'Tag RC{next_rc_num}'"
82-
f" to cut {version}-rc{next_rc_num}."
83-
)
84-
return 1
85-
86-
target_rc_task = rc_tags[next_rc_num]
87-
if target_rc_task.checked or target_rc_task.status == "done":
88-
print(
89-
f"Error: Task 'Tag RC{next_rc_num}' is already marked done in"
90-
" the checklist."
91-
)
92-
return 1
81+
print(f"Task 'Tag RC{next_rc_num}' not found in checklist. Adding it...")
82+
body = add_rc_task_to_body(body, next_rc_num)
83+
self.gh.update_issue_body(args.issue, body)
84+
else:
85+
target_rc_task = rc_tags[next_rc_num]
86+
if target_rc_task.checked or target_rc_task.status == "done":
87+
print(
88+
f"Error: Task 'Tag RC{next_rc_num}' is already marked done in"
89+
" the checklist."
90+
)
91+
return 1
9392

9493
target_ref = f"{args.remote}/{branch_name}"
9594
commit_sha = self.git.get_commit_sha(target_ref)

tools/private/release/release_issue.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,32 @@ def add_backports_to_body(body: str, prs: list[int]) -> str:
284284
# Replace the old section with the updated one
285285
start, end = match.span(2)
286286
return body[:start] + updated_section + body[end:]
287+
288+
289+
def add_rc_task_to_body(body: str, rc_num: int) -> str:
290+
"""Adds a new 'Tag RC<rc_num>' task to the checklist in the issue body."""
291+
body = body.replace("\r\n", "\n")
292+
lines = body.splitlines()
293+
294+
# Find the index of the last "Tag RC<M>" line
295+
last_rc_idx = -1
296+
for i, line in enumerate(lines):
297+
parsed = parse_metadata_line(line)
298+
if parsed and re.match(r"Tag RC\d+", parsed["name"], re.IGNORECASE):
299+
last_rc_idx = i
300+
301+
if last_rc_idx == -1:
302+
# If no RC task found (unexpected, but fallback to before "Tag Final")
303+
for i, line in enumerate(lines):
304+
parsed = parse_metadata_line(line)
305+
if parsed and parsed["name"].lower() == "tag final":
306+
last_rc_idx = i - 1
307+
break
308+
309+
if last_rc_idx == -1:
310+
raise ValueError("Could not find a place to insert the new RC task.")
311+
312+
new_task_line = f"- [ ] Tag RC{rc_num}"
313+
lines.insert(last_rc_idx + 1, new_task_line)
314+
315+
return "\n".join(lines)

0 commit comments

Comments
 (0)