| """Subcommand to mark sync changelog tasks as complete.""" | |
| import logging | |
| import re | |
| from dev.release.gh import GitHub, get_github_event_pr_number | |
| from dev.release.release_issue import ( | |
| parse_checklist_state, | |
| update_task_in_body, | |
| ) | |
| logger = logging.getLogger(__name__) | |
| class CompleteSyncChangelog: | |
| """Class to mark sync changelog tasks as complete.""" | |
| def __init__(self, args, gh: GitHub): | |
| self.args = args | |
| self.gh = gh | |
| def run(self) -> int: | |
| """Executes the complete-sync-changelog subcommand.""" | |
| args = self.args | |
| pr_num = args.pr or get_github_event_pr_number() | |
| if not pr_num: | |
| logger.error( | |
| "No PR specified and could not extract PR number from GITHUB_EVENT_PATH." | |
| ) | |
| return 1 | |
| logger.info("Completing sync changelog for PR #%d...", pr_num) | |
| pr_info = self.gh.get_pr_info(pr_num) | |
| if not pr_info or pr_info.get("state") != "MERGED": | |
| state = pr_info.get("state", "UNKNOWN") | |
| logger.error("PR #%d is not merged yet (state: %s).", pr_num, state) | |
| return 1 | |
| # Resolve issue number from PR body using Release-Tracking-Issue: #<issue> | |
| pr_body = pr_info.get("body") or "" | |
| match = re.search(r"Release-Tracking-Issue:\s*#(\d+)", pr_body) | |
| if not match: | |
| logger.error( | |
| "Could not find 'Release-Tracking-Issue: #<issue>' in PR #%d body: %s", | |
| pr_num, | |
| pr_body, | |
| ) | |
| return 1 | |
| issue_num = int(match.group(1)) | |
| logger.info("Resolved tracking issue #%d from PR #%d body.", issue_num, pr_num) | |
| commit_sha = pr_info["mergeCommit"]["oid"] | |
| short_commit = commit_sha[:8] | |
| logger.info( | |
| "PR #%d merged at commit %s. Updating tracking issue...", | |
| pr_num, | |
| commit_sha, | |
| ) | |
| # Update checklist: mark all Sync Changelog tasks pointing to this PR as done | |
| body = self.gh.get_issue_body(issue_num) | |
| state = parse_checklist_state(body) | |
| sync_changelogs = state.get("sync_changelogs", {}) | |
| updated_any = False | |
| for target_pr_num, task in sync_changelogs.items(): | |
| # Check if this task points to our merged PR | |
| task_pr = task.metadata.get("pr") | |
| if task_pr == f"#{pr_num}": | |
| logger.info("Marking task '%s' as complete...", task.name) | |
| metadata = { | |
| "status": "done", | |
| "pr": f"#{pr_num}", | |
| "commit": short_commit, | |
| } | |
| body = update_task_in_body( | |
| body, task.name, checked=True, metadata=metadata | |
| ) | |
| updated_any = True | |
| if not updated_any: | |
| logger.warning("No 'Sync Changelog' tasks found pointing to PR #%d", pr_num) | |
| return 0 | |
| self.gh.update_issue_body(issue_num, body) | |
| logger.info("Sync changelog tasks marked complete successfully!") | |
| return 0 | |
| @classmethod | |
| def add_parser(cls, subparsers): | |
| """Adds parser for complete-sync-changelog subcommand.""" | |
| parser = subparsers.add_parser( | |
| "complete-sync-changelog", | |
| help="Mark the Sync Changelog tasks as complete in the tracking issue.", | |
| ) | |
| parser.add_argument( | |
| "--pr", | |
| type=int, | |
| help="The merged sync changelog PR number (optional; extracted from GITHUB_EVENT_PATH if omitted).", | |
| ) | |
| parser.set_defaults(command=cls.run_from_args) | |
| @classmethod | |
| def run_from_args(cls, args): | |
| """Instantiates and runs the command from parsed args.""" | |
| gh = GitHub() | |
| return cls(args, gh).run() |