Merge pull request #47857 from ClickHouse/cherry-pick-ping

Comment stale cherry-pick PRs once a day to remind for resolving conflicts
This commit is contained in:
Mikhail f. Shiryaev 2023-03-22 13:36:35 +03:00 committed by GitHub
commit 6409ca9fc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -27,7 +27,7 @@ import argparse
import logging import logging
import os import os
from contextlib import contextmanager from contextlib import contextmanager
from datetime import date, timedelta from datetime import date, datetime, timedelta
from subprocess import CalledProcessError from subprocess import CalledProcessError
from typing import List, Optional from typing import List, Optional
@ -123,7 +123,9 @@ close it.
# Going from the tail to keep the order and pop greater index first # Going from the tail to keep the order and pop greater index first
prs.pop(i) prs.pop(i)
def process(self, dry_run: bool) -> None: def process( # pylint: disable=too-many-return-statements
self, dry_run: bool
) -> None:
if self.backported: if self.backported:
return return
if not self.cherrypick_pr: if not self.cherrypick_pr:
@ -138,6 +140,11 @@ close it.
if self.cherrypick_pr is not None: if self.cherrypick_pr is not None:
# Try to merge cherrypick instantly # Try to merge cherrypick instantly
if self.cherrypick_pr.mergeable and self.cherrypick_pr.state != "closed": if self.cherrypick_pr.mergeable and self.cherrypick_pr.state != "closed":
if dry_run:
logging.info(
"DRY RUN: Would merge cherry-pick PR for #%s", self.pr.number
)
return
self.cherrypick_pr.merge() self.cherrypick_pr.merge()
# The PR needs update, since PR.merge doesn't update the object # The PR needs update, since PR.merge doesn't update the object
self.cherrypick_pr.update() self.cherrypick_pr.update()
@ -149,7 +156,7 @@ close it.
return return
self.create_backport() self.create_backport()
return return
elif self.cherrypick_pr.state == "closed": if self.cherrypick_pr.state == "closed":
logging.info( logging.info(
"The cherrypick PR #%s for PR #%s is discarded", "The cherrypick PR #%s for PR #%s is discarded",
self.cherrypick_pr.number, self.cherrypick_pr.number,
@ -162,6 +169,7 @@ close it.
self.cherrypick_pr.number, self.cherrypick_pr.number,
self.pr.number, self.pr.number,
) )
self.ping_cherry_pick_assignees(dry_run)
def create_cherrypick(self): def create_cherrypick(self):
# First, create backport branch: # First, create backport branch:
@ -227,6 +235,7 @@ close it.
assert self.cherrypick_pr is not None assert self.cherrypick_pr is not None
# Checkout the backport branch from the remote and make all changes to # Checkout the backport branch from the remote and make all changes to
# apply like they are only one cherry-pick commit on top of release # apply like they are only one cherry-pick commit on top of release
logging.info("Creating backport for PR #%s", self.pr.number)
git_runner(f"{self.git_prefix} checkout -f {self.backport_branch}") git_runner(f"{self.git_prefix} checkout -f {self.backport_branch}")
git_runner( git_runner(
f"{self.git_prefix} pull --ff-only {self.REMOTE} {self.backport_branch}" f"{self.git_prefix} pull --ff-only {self.REMOTE} {self.backport_branch}"
@ -255,6 +264,40 @@ close it.
self.backport_pr.add_to_labels(Labels.BACKPORT) self.backport_pr.add_to_labels(Labels.BACKPORT)
self._assign_new_pr(self.backport_pr) self._assign_new_pr(self.backport_pr)
def ping_cherry_pick_assignees(self, dry_run: bool) -> None:
assert self.cherrypick_pr is not None
logging.info(
"Checking if cherry-pick PR #%s needs to be pinged",
self.cherrypick_pr.number,
)
since_updated = datetime.now() - self.cherrypick_pr.updated_at
since_updated_str = (
f"{since_updated.days}d{since_updated.seconds // 3600}"
f"h{since_updated.seconds // 60 % 60}m{since_updated.seconds % 60}s"
)
if since_updated < timedelta(days=1):
logging.info(
"The cherry-pick PR was updated at %s %s ago, "
"waiting for the next running",
self.cherrypick_pr.updated_at.isoformat(),
since_updated_str,
)
return
assignees = ", ".join(f"@{user.login}" for user in self.cherrypick_pr.assignees)
comment_body = (
f"Dear {assignees}, the PR is not updated for {since_updated_str}. "
"Please, either resolve the conflicts, or close it to finish "
f"the backport process of #{self.pr.number}"
)
if dry_run:
logging.info(
"DRY RUN: would comment the cherry-pick PR #%s:\n",
self.cherrypick_pr.number,
)
return
self.cherrypick_pr.create_issue_comment(comment_body)
def _assign_new_pr(self, new_pr: PullRequest) -> None: def _assign_new_pr(self, new_pr: PullRequest) -> None:
"""Assign `new_pr` to author, merger and assignees of an original PR""" """Assign `new_pr` to author, merger and assignees of an original PR"""
# It looks there some race when multiple .add_to_assignees are executed, # It looks there some race when multiple .add_to_assignees are executed,