build(release): comment on PRs when added to release tracking issue (#4302) Previously, adding a PR to a release tracking issue's backports checklist left no notification on the PR itself, making it harder to see which release tracks the backport. Update `add-backports` to update the tracking issue per PR and post a comment on each backported PR linking the created or updated release tracking issue, and grant `pull-requests: write` permission to the workflow.
diff --git a/.github/workflows/release_add_backports.yaml b/.github/workflows/release_add_backports.yaml index b20fd95..db28a03 100644 --- a/.github/workflows/release_add_backports.yaml +++ b/.github/workflows/release_add_backports.yaml
@@ -24,6 +24,7 @@ permissions: issues: write + pull-requests: write jobs: add_backports:
diff --git a/dev/release/add_backports.py b/dev/release/add_backports.py index 8d357e8..1a10e46 100644 --- a/dev/release/add_backports.py +++ b/dev/release/add_backports.py
@@ -26,6 +26,7 @@ args = self.args issue_num = args.issue + created_issue = False if not issue_num: print( "No issue specified. Trying to auto-discover active release" @@ -64,6 +65,7 @@ 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}" @@ -74,59 +76,75 @@ ) return 1 - resolved_prs = [] - for pr_ref in args.prs: - try: - pr_num = self.gh.resolve_pr_number(pr_ref) - resolved_prs.append(pr_num) - 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() - ) - 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 - try: - self.gh.update_issue_body(issue_num, body) + 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 = 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"::notice::Successfully updated tracking issue #{issue_num} checklist." + f"Adding backport #{pr} (resolved from {pr_ref}) to tracking" + f" issue #{issue_num}..." ) - except Exception as e: - print(f"::error::Failed to update tracking issue body: {e}") - return 1 + 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 + + 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 + + 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
diff --git a/tests/tools/private/release/add_backports_test.py b/tests/tools/private/release/add_backports_test.py index 24636c6..3b5ece7 100644 --- a/tests/tools/private/release/add_backports_test.py +++ b/tests/tools/private/release/add_backports_test.py
@@ -30,6 +30,12 @@ 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): @@ -51,6 +57,10 @@ 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( @@ -75,6 +85,10 @@ 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(