Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions tests/tools/private/release/create_rc_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import argparse
import os
import pathlib
import tempfile
Expand Down Expand Up @@ -212,6 +213,46 @@ def test_create_rc_with_finished_backports(self):
self.mock_git.tag.assert_called_once_with("2.0.0-rc0", "my-remote/release/2.0")
self.mock_git.push.assert_called_once_with("my-remote", "2.0.0-rc0")

def test_create_rc_auto_add_task(self):
# Arrange
args = argparse.Namespace(issue=123, remote="my-remote")
self.mock_gh.get_issue_title.return_value = "Release 2.0.0"
self.mock_gh.get_issue_body.return_value = """
## Checklist
- [x] Prepare Release | status=done pr=#122 commit=abcdef12
- [x] Create Release branch | status=done branch=release/2.0 commit=abcdef12
- [x] Tag RC0 | status=done tag=2.0.0-rc0 commit=abcdef12
- [ ] Tag Final
"""
self.mock_git.get_remote_tags.return_value = ["2.0.0-rc0"]
self.mock_git.get_commit_sha.return_value = "1234567890"

# Act
result = CreateRc(args, self.mock_git, self.mock_gh).run()

# Assert
self.assertEqual(result, 0)
self.mock_git.tag.assert_called_once_with("2.0.0-rc1", "my-remote/release/2.0")
self.mock_git.push.assert_called_once_with("my-remote", "2.0.0-rc1")

self.assertEqual(self.mock_gh.update_issue_body.call_count, 2)
call1_args = self.mock_gh.update_issue_body.call_args_list[0][0]
call2_args = self.mock_gh.update_issue_body.call_args_list[1][0]

self.assertEqual(call1_args[0], 123)
self.assertIn("- [ ] Tag RC1", call1_args[1])
self.assertIn(
"- [x] Tag RC0 | status=done tag=2.0.0-rc0 commit=abcdef12\n- [ ]"
" Tag RC1\n- [ ] Tag Final",
call1_args[1].strip(),
)

self.assertEqual(call2_args[0], 123)
self.assertIn(
"- [x] Tag RC1 | status=done tag=2.0.0-rc1 commit= 12345678",
call2_args[1],
)
Comment thread
rickeylev marked this conversation as resolved.


if __name__ == "__main__":
unittest.main()
25 changes: 12 additions & 13 deletions tools/private/release/create_rc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from tools.private.release.git import Git
from tools.private.release.release_issue import (
RELEASE_TITLE_RE,
add_rc_task_to_body,
parse_backports,
parse_checklist_state,
update_task_in_body,
Expand Down Expand Up @@ -77,19 +78,17 @@ def run(self) -> int:
# Precheck: next RC number must exist and be unchecked in the checklist
rc_tags = state.get("rc_tags", {})
if next_rc_num not in rc_tags:
print(
f"Error: Checklist is missing required task 'Tag RC{next_rc_num}'"
f" to cut {version}-rc{next_rc_num}."
)
return 1

target_rc_task = rc_tags[next_rc_num]
if target_rc_task.checked or target_rc_task.status == "done":
print(
f"Error: Task 'Tag RC{next_rc_num}' is already marked done in"
" the checklist."
)
return 1
print(f"Task 'Tag RC{next_rc_num}' not found in checklist. Adding it...")
body = add_rc_task_to_body(body, next_rc_num)
self.gh.update_issue_body(args.issue, body)
Comment thread
rickeylev marked this conversation as resolved.
else:
target_rc_task = rc_tags[next_rc_num]
if target_rc_task.checked or target_rc_task.status == "done":
print(
f"Error: Task 'Tag RC{next_rc_num}' is already marked done in"
" the checklist."
)
return 1

target_ref = f"{args.remote}/{branch_name}"
commit_sha = self.git.get_commit_sha(target_ref)
Expand Down
29 changes: 29 additions & 0 deletions tools/private/release/release_issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,32 @@ def add_backports_to_body(body: str, prs: list[int]) -> str:
# Replace the old section with the updated one
start, end = match.span(2)
return body[:start] + updated_section + body[end:]


def add_rc_task_to_body(body: str, rc_num: int) -> str:
"""Adds a new 'Tag RC<rc_num>' task to the checklist in the issue body."""
body = body.replace("\r\n", "\n")
lines = body.splitlines()

# Find the index of the last "Tag RC<M>" line
last_rc_idx = -1
for i, line in enumerate(lines):
parsed = parse_metadata_line(line)
if parsed and re.match(r"Tag RC\d+", parsed["name"], re.IGNORECASE):
last_rc_idx = i

if last_rc_idx == -1:
# If no RC task found (unexpected, but fallback to before "Tag Final")
for i, line in enumerate(lines):
parsed = parse_metadata_line(line)
if parsed and parsed["name"].lower() == "tag final":
last_rc_idx = i - 1
break

if last_rc_idx == -1:
raise ValueError("Could not find a place to insert the new RC task.")

new_task_line = f"- [ ] Tag RC{rc_num}"
lines.insert(last_rc_idx + 1, new_task_line)
Comment thread
rickeylev marked this conversation as resolved.

return "\n".join(lines)