Repository: ClickHouse/ClickHouse
File: tests/ci/cherry_pick.py (Python)

Code before:
        since_updated = int(datetime.now().timestamp() - cherrypick_updated_ts)
        since_updated_str = (
            f"{since_updated // 86400}d{since_updated // 3600 % 24}h"
            f"{since_updated // 60 % 60}m{since_updated % 60}s"
        )

        PING_THRESHOLD = 3 * 24 * 3600  # 3 days
        CLOSE_THRESHOLD = 7 * 24 * 3600  # 7 days

        if since_updated < PING_THRESHOLD:
            logging.info(
                "The cherry-pick PR was updated %s ago, waiting for the next run",
                since_updated_str,
            )
            return

        if since_updated >= CLOSE_THRESHOLD:
            # Close the PR after 7 days
            assignees = ", ".join(
                f"@{user.login}" for user in self.cherrypick_pr.assignees
            )
            comment_body = (
                f"Dear {assignees}, this cherry-pick PR has not been updated for {since_updated_str}. "
                f"Closing automatically. If you still want to backport #{self.pr.number}, "
                "please resolve the conflicts and reopen this PR."
            )
            if dry_run:
                logging.info(
                    "DRY RUN: would close cherry-pick PR #%s with comment:\n%s",
                    self.cherrypick_pr.number,
                    comment_body,
                )
                return
            self.cherrypick_pr.create_issue_comment(comment_body)
            self.cherrypick_pr.edit(state="closed")
            return

        # Ping after 3 days
        # Check if we've already pinged to avoid spamming
        comments = self.cherrypick_pr.get_issue_comments()
        for comment in comments:
            if (
                "has not been updated for" in comment.body
                and "resolve the conflicts" in comment.body
            ):
                # We've already pinged, don't ping again
                logging.info(
                    "Already pinged cherry-pick PR #%s, waiting for update or closure threshold",
                    self.cherrypick_pr.number,
                )
                return

Review comment (bug):
If `self.cherrypick_pr.assignees` is empty (e.g., manual unassign, assignment API failure, permission issues), the bot will post `Dear , ...` and won’t actually notify anyone. Consider handling the empty case explicitly (e.g., assign at least one fallback reviewer/author before commenting, or omit the greeting and include another notification mechanism).

Code after:
        since_updated = int(datetime.now().timestamp() - cherrypick_updated_ts)
        since_updated_str = (
            f"{since_updated // 86400}d{since_updated // 3600 % 24}h"
            f"{since_updated // 60 % 60}m{since_updated % 60}s"
        )

        PING_THRESHOLD = 3 * 24 * 3600  # 3 days
        CLOSE_THRESHOLD = 7 * 24 * 3600  # 7 days

        if since_updated < PING_THRESHOLD:
            logging.info(
                "The cherry-pick PR was updated %s ago, waiting for the next run",
                since_updated_str,
            )
            return

        if since_updated >= CLOSE_THRESHOLD:
            # Close the PR after 7 days
            if self.cherrypick_pr.assignees:
                assignees = ", ".join(
                    f"@{user.login}" for user in self.cherrypick_pr.assignees
                )
                comment_body = (
                    f"Dear {assignees}, this cherry-pick PR has not been updated for {since_updated_str}. "
                    f"Closing automatically. If you still want to backport #{self.pr.number}, "
                    "please resolve the conflicts and reopen this PR."
                )
            else:
                logging.warning(
                    "Cherry-pick PR #%s has no assignees when closing",
                    self.cherrypick_pr.number,
                )
                comment_body = (
                    f"This cherry-pick PR has not been updated for {since_updated_str}. "
                    f"Closing automatically. If you still want to backport #{self.pr.number}, "
                    "please resolve the conflicts and reopen this PR."
                )
            if dry_run:
                logging.info(
                    "DRY RUN: would close cherry-pick PR #%s with comment:\n%s",
                    self.cherrypick_pr.number,
                    comment_body,
                )
                return
            self.cherrypick_pr.create_issue_comment(comment_body)
            logging.info(
                "Posted closing comment to cherry-pick PR #%s",
                self.cherrypick_pr.number,
            )
            self.cherrypick_pr.edit(state="closed")
            logging.info(