Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions .github/workflows/release_add_backports.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ on:

permissions:
issues: write
pull-requests: write

jobs:
add_backports:
Expand Down
100 changes: 59 additions & 41 deletions dev/release/add_backports.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def run(self) -> int:
args = self.args

issue_num = args.issue
created_issue = False
if not issue_num:
print(
"No issue specified. Trying to auto-discover active release"
Expand Down Expand Up @@ -64,6 +65,7 @@ def run(self) -> int:
issue_num = self.gh.create_release_tracking_issue(
patch_version, template_content
)
created_issue = True
print(
f"::notice::Created patch release tracking issue #{issue_num} for"
f" v{patch_version}"
Expand All @@ -74,59 +76,75 @@ def run(self) -> int:
)
return 1

resolved_prs = []
try:
body = self.gh.get_issue_body(issue_num)
issue_title = self.gh.get_issue_title(issue_num)
except Exception as e:
print(f"::error::Failed to update tracking issue: {e}")
return 1

version_match = RELEASE_TITLE_RE.search(issue_title)
is_patch = False
if version_match:
version = version_match.group(1)
is_patch = not version.endswith(".0")

action = "Created" if created_issue else "Updated"
comment_body = (
f"{action} release tracking issue #{issue_num} ({issue_title}) with"
" this PR added to the backports checklist."
)

for pr_ref in args.prs:
try:
pr_num = self.gh.resolve_pr_number(pr_ref)
resolved_prs.append(pr_num)
pr = self.gh.resolve_pr_number(pr_ref)
except Exception as e:
print(f"::error::Error resolving PR ref '{pr_ref}': {e}")
return 1

print(
f"Adding backports {resolved_prs} (resolved from {args.prs}) to tracking issue #{issue_num}..."
)
try:
body = self.gh.get_issue_body(issue_num)
items_to_add = [{"ref": f"#{pr}"} for pr in resolved_prs]
body = add_backports_to_body(body, items_to_add)
for pr in resolved_prs:
body = add_sync_changelog_task_to_body(body, pr)
state = parse_checklist_state(body)
rc_tags = state.get("rc_tags", {})
has_pending_rc = any(
not task.checked and task.status != "done" for task in rc_tags.values()
print(
f"Adding backport #{pr} (resolved from {pr_ref}) to tracking"
f" issue #{issue_num}..."
)
next_rc_num = max(rc_tags.keys()) + 1 if rc_tags else 0
try:
body = add_backports_to_body(body, [{"ref": f"#{pr}"}])
body = add_sync_changelog_task_to_body(body, pr)
state = parse_checklist_state(body)
rc_tags = state.get("rc_tags", {})
has_pending_rc = any(
not task.checked and task.status != "done"
for task in rc_tags.values()
)
next_rc_num = max(rc_tags.keys()) + 1 if rc_tags else 0

issue_title = self.gh.get_issue_title(issue_num)
version_match = RELEASE_TITLE_RE.search(issue_title)
is_patch = False
if version_match:
version = version_match.group(1)
is_patch = not version.endswith(".0")
if not has_pending_rc and (rc_tags or not is_patch):
print(
f"No pending RC task found. Adding 'Tag"
f" RC{next_rc_num}' to checklist..."
)
body = add_rc_task_to_body(body, next_rc_num)
except ValueError as e:
print(f"::error::{e}")
return 1
except Exception as e:
print(f"::error::Failed to update tracking issue: {e}")
return 1

if not has_pending_rc and (rc_tags or not is_patch):
try:
self.gh.update_issue_body(issue_num, body)
print(
f"No pending RC task found. Adding 'Tag"
f" RC{next_rc_num}' to checklist..."
f"::notice::Successfully updated tracking issue #{issue_num}"
" checklist."
)
body = add_rc_task_to_body(body, next_rc_num)
except ValueError as e:
print(f"::error::{e}")
return 1
except Exception as e:
print(f"::error::Failed to update tracking issue: {e}")
return 1
except Exception as e:
print(f"::error::Failed to update tracking issue body: {e}")
return 1

try:
self.gh.update_issue_body(issue_num, body)
print(
f"::notice::Successfully updated tracking issue #{issue_num} checklist."
)
except Exception as e:
print(f"::error::Failed to update tracking issue body: {e}")
return 1
try:
self.gh.post_issue_comment(pr, comment_body)
except Exception as e:
print(f"::error::Failed to post comment to PR #{pr}: {e}")
return 1

return 0

Expand Down
14 changes: 14 additions & 0 deletions tests/tools/private/release/add_backports_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ def test_add_backports_explicit_issue(mock_gh):
assert "- [ ] Tag RC0" in updated_body
assert "- [ ] Sync Changelog #124" in updated_body
assert "- [ ] Sync Changelog #125" in updated_body
expected_comment = (
"Updated release tracking issue #123 (Release 2.1.0) with this PR added"
" to the backports checklist."
)
assert mock_gh.issue_comments[124] == [expected_comment]
assert mock_gh.issue_comments[125] == [expected_comment]


def test_add_backports_auto_discover_success(mock_gh):
Expand All @@ -51,6 +57,10 @@ def test_add_backports_auto_discover_success(mock_gh):
assert result == 0
updated_body = mock_gh.get_issue_body(issue_num)
assert "- [ ] #124" in updated_body
assert mock_gh.issue_comments[124] == [
f"Updated release tracking issue #{issue_num} (Release 2.1.0) with this"
" PR added to the backports checklist."
]


def test_add_backports_auto_discover_no_issues_creates_patch_release(
Expand All @@ -75,6 +85,10 @@ def test_add_backports_auto_discover_no_issues_creates_patch_release(
assert "Prepare Release" not in body
assert "Create Release branch" not in body
assert "- [ ] Tag Final" in body
assert mock_gh.issue_comments[124] == [
f"Created release tracking issue #{issue['number']} (Release 1.2.1)"
" with this PR added to the backports checklist."
]


def test_add_backports_auto_discover_skips_complete_release_creates_patch_release(
Expand Down
Loading