Skip to content

Commit

Permalink
feat: Remove 插件新增 auto_merge 功能
Browse files Browse the repository at this point in the history
  • Loading branch information
BigOrangeQWQ committed Nov 8, 2024
1 parent 6804189 commit e373c5d
Show file tree
Hide file tree
Showing 8 changed files with 446 additions and 94 deletions.
31 changes: 30 additions & 1 deletion src/plugins/github/depends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
)
from nonebot.params import Depends

from src.plugins.github.models import GithubHandler, RepoInfo
from src.plugins.github.models import GithubHandler, IssueHandler, RepoInfo
from src.plugins.github.typing import IssuesEvent, LabelsItems, PullRequestEvent
from src.plugins.github.utils import run_shell_command
from src.providers.validation.models import PublishType
Expand Down Expand Up @@ -107,6 +107,35 @@ def get_github_handler(bot: GitHubBot, repo_info: RepoInfo = Depends(get_repo_in
return GithubHandler(bot=bot, repo_info=repo_info)


async def get_issue_handler(
bot: GitHubBot,
installation_id: int = Depends(get_installation_id),
repo_info: RepoInfo = Depends(get_repo_info),
issue_number: int = Depends(get_issue_number),
):
async with bot.as_installation(installation_id):
# 因为 Actions 会排队,触发事件相关的议题在 Actions 执行时可能已经被关闭
# 所以需要获取最新的议题状态
issue = (
await bot.rest.issues.async_get(
**repo_info.model_dump(), issue_number=issue_number
)
).parsed_data

return IssueHandler(bot=bot, repo_info=repo_info, issue=issue)


async def get_related_issue_handler(
bot: GitHubBot,
installation_id: int = Depends(get_installation_id),
repo_info: RepoInfo = Depends(get_repo_info),
related_issue_number: int = Depends(get_related_issue_number),
):
return await get_issue_handler(
bot, installation_id, repo_info, related_issue_number
)


def get_type_by_labels_name(
labels: list[str] = Depends(get_labels_name),
) -> PublishType | None:
Expand Down
8 changes: 4 additions & 4 deletions src/plugins/github/plugins/publish/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
bypass_git,
get_github_handler,
get_installation_id,
get_issue_handler,
get_labels_name,
get_related_issue_handler,
get_related_issue_number,
get_repo_info,
install_pre_commit_hooks,
Expand All @@ -31,8 +33,6 @@
from src.providers.validation.models import PublishType, ValidationDict

from .depends import (
get_issue_handler,
get_related_issue_handler,
get_type_by_labels_name,
)
from .utils import (
Expand Down Expand Up @@ -250,7 +250,7 @@ async def handle_pull_request_and_update_issue(
await handler.comment_issue(comment)


async def review_submiited_rule(
async def review_submitted_rule(
event: PullRequestReviewSubmitted,
publish_type: PublishType | None = Depends(get_type_by_labels_name),
) -> bool:
Expand All @@ -268,7 +268,7 @@ async def review_submiited_rule(


auto_merge_matcher = on_type(
PullRequestReviewSubmitted, rule=Rule(review_submiited_rule, publish_related_rule)
PullRequestReviewSubmitted, rule=Rule(review_submitted_rule, publish_related_rule)
)


Expand Down
34 changes: 1 addition & 33 deletions src/plugins/github/plugins/publish/depends.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@
from nonebot.params import Depends

from src.plugins.github.depends import (
get_installation_id,
get_issue_number,
get_issue_title,
get_related_issue_number,
get_repo_info,
get_type_by_labels_name,
)
from src.plugins.github.models import IssueHandler, RepoInfo
from src.plugins.github.models import RepoInfo
from src.plugins.github.plugins.publish import utils
from src.providers.validation.models import PublishType

Expand All @@ -33,32 +30,3 @@ async def get_pull_requests_by_label(
for pull in pulls
if publish_type.value in [label.name for label in pull.labels]
]


async def get_issue_handler(
bot: GitHubBot,
installation_id: int = Depends(get_installation_id),
repo_info: RepoInfo = Depends(get_repo_info),
issue_number: int = Depends(get_issue_number),
):
async with bot.as_installation(installation_id):
# 因为 Actions 会排队,触发事件相关的议题在 Actions 执行时可能已经被关闭
# 所以需要获取最新的议题状态
issue = (
await bot.rest.issues.async_get(
**repo_info.model_dump(), issue_number=issue_number
)
).parsed_data

return IssueHandler(bot=bot, repo_info=repo_info, issue=issue)


async def get_related_issue_handler(
bot: GitHubBot,
installation_id: int = Depends(get_installation_id),
repo_info: RepoInfo = Depends(get_repo_info),
related_issue_number: int = Depends(get_related_issue_number),
):
return await get_issue_handler(
bot, installation_id, repo_info, related_issue_number
)
85 changes: 60 additions & 25 deletions src/plugins/github/plugins/remove/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
IssuesOpened,
IssuesReopened,
PullRequestClosed,
PullRequestReviewSubmitted,
)
from nonebot.params import Depends
from pydantic_core import PydanticCustomError
Expand All @@ -15,8 +16,10 @@
from src.plugins.github.depends import (
RepoInfo,
bypass_git,
get_github_handler,
get_installation_id,
get_issue_number,
get_issue_handler,
get_related_issue_handler,
get_related_issue_number,
get_repo_info,
get_type_by_labels_name,
Expand Down Expand Up @@ -60,22 +63,13 @@ async def handle_pr_close(
event: PullRequestClosed,
bot: GitHubBot,
installation_id: int = Depends(get_installation_id),
repo_info: RepoInfo = Depends(get_repo_info),
related_issue_number: int = Depends(get_related_issue_number),
handler: IssueHandler = Depends(get_related_issue_handler),
) -> None:
async with bot.as_installation(installation_id):
issue = (
await bot.rest.issues.async_get(
**repo_info.model_dump(), issue_number=related_issue_number
)
).parsed_data

handler = IssueHandler(bot=bot, repo_info=repo_info, issue=issue)

if issue.state == "open":
if handler.issue.state == "open":
reason = "completed" if event.payload.pull_request.merged else "not_planned"
await handler.close_issue(reason)
logger.info(f"议题 #{related_issue_number} 已关闭")
logger.info(f"议题 #{handler.issue_number} 已关闭")

try:
handler.delete_origin_branch(event.payload.pull_request.head.ref)
Expand Down Expand Up @@ -119,25 +113,17 @@ async def check_rule(
async def handle_remove_check(
bot: GitHubBot,
installation_id: int = Depends(get_installation_id),
repo_info: RepoInfo = Depends(get_repo_info),
issue_number: int = Depends(get_issue_number),
handler: IssueHandler = Depends(get_issue_handler),
publish_type: PublishType = Depends(get_type_by_labels_name),
):
async with bot.as_installation(installation_id):
issue = (
await bot.rest.issues.async_get(
**repo_info.model_dump(), issue_number=issue_number
)
).parsed_data

if issue.state != "open":
if handler.issue.state != "open":
logger.info("议题未开启,已跳过")
await remove_check_matcher.finish()
handler = IssueHandler(bot=bot, repo_info=repo_info, issue=issue)

try:
# 搜索包的信息和验证作者信息
result = await validate_author_info(issue, publish_type)
result = await validate_author_info(handler.issue, publish_type)
except PydanticCustomError as err:
logger.error(f"信息验证失败: {err}")
await handler.comment_issue(await render_error(err))
Expand All @@ -146,7 +132,7 @@ async def handle_remove_check(
title = f"{result.publish_type}: Remove {result.name or 'Unknown'}"[
:TITLE_MAX_LENGTH
]
branch_name = f"{BRANCH_NAME_PREFIX}{issue_number}"
branch_name = f"{BRANCH_NAME_PREFIX}{handler.issue_number}"

# 根据 input_config 里的 remove 仓库来进行提交和 PR
store_handler = GithubHandler(
Expand All @@ -168,3 +154,52 @@ async def handle_remove_check(
pr_url=f"{plugin_config.input_config.store_repository}#{pull_number}",
)
)


async def review_submitted_rule(
event: PullRequestReviewSubmitted,
is_remove: bool = check_labels(REMOVE_LABEL),
) -> bool:
if not is_remove:
logger.info("拉取请求与删除无关,已跳过")
return False
if event.payload.review.author_association not in ["OWNER", "MEMBER"]:
logger.info("审查者不是仓库成员,已跳过")
return False
if event.payload.review.state != "approved":
logger.info("未通过审查,已跳过")
return False

return True


auto_merge_matcher = on_type(PullRequestReviewSubmitted, rule=review_submitted_rule)


@auto_merge_matcher.handle(
parameterless=[Depends(bypass_git), Depends(install_pre_commit_hooks)]
)
async def handle_auto_merge(
bot: GitHubBot,
event: PullRequestReviewSubmitted,
installation_id: int = Depends(get_installation_id),
repo_info: RepoInfo = Depends(get_repo_info),
handler: GithubHandler = Depends(get_github_handler),
) -> None:
async with bot.as_installation(installation_id):
pull_request = (
await bot.rest.pulls.async_get(
**repo_info.model_dump(), pull_number=event.payload.pull_request.number
)
).parsed_data

if not pull_request.mergeable:
# 尝试处理冲突
await resolve_conflict_pull_requests(handler, [pull_request])

await bot.rest.pulls.async_merge(
**repo_info.model_dump(),
pull_number=event.payload.pull_request.number,
merge_method="rebase",
)
logger.info(f"已自动合并 #{event.payload.pull_request.number}")
17 changes: 4 additions & 13 deletions src/plugins/github/plugins/remove/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from githubkit.exception import RequestFailed
from nonebot import logger
from pydantic_core import PydanticCustomError

from src.plugins.github import plugin_config
from src.plugins.github.depends.utils import (
Expand Down Expand Up @@ -105,23 +104,15 @@ async def resolve_conflict_pull_requests(
if publish_type:
# 需要先获取远程分支,否则无法切换到对应分支
run_shell_command(["git", "fetch", "origin"])
# 因为当前分支为触发处理冲突的分支,所以需要切换到每个拉取请求对应的分支
run_shell_command(["git", "checkout", pull.head.ref])

try:
result = await validate_author_info(issue_handler.issue, publish_type)
except PydanticCustomError:
# 报错代表在此分支找不到对应数据
# 则尝试处理其它分支
logger.info("拉取请求无冲突,无需处理")
continue

# 回到主分支
# 当前分支未触发处理冲突的分支,切换到主分支后验证其它的删除请求
run_shell_command(["git", "checkout", plugin_config.input_config.base])
# 再验证作者信息
result = await validate_author_info(issue_handler.issue, publish_type)
# 切换到对应分支
run_shell_command(["git", "switch", "-C", pull.head.ref])
# 更新文件
update_file(publish_type, result.key)
# 生成提交信息并推送
message = commit_message(COMMIT_MESSAGE_PREFIX, result.name, issue_number)

issue_handler.commit_and_push(message, pull.head.ref)
Expand Down
Loading

0 comments on commit e373c5d

Please sign in to comment.