Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/lang/zh-CN/

## [Unreleased]

### Fixed

- 修复 git 没有权限推送的问题

## [4.4.4] - 2025-08-10

### Added
Expand Down
44 changes: 33 additions & 11 deletions src/plugins/github/depends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,6 @@
from .utils import extract_issue_number_from_ref


async def bypass_git():
"""绕过检查"""
# https://github.blog/2022-04-18-highlights-from-git-2-36/#stricter-repository-ownership-checks
run_shell_command(["git", "config", "--global", "safe.directory", "*"])


async def install_pre_commit_hooks():
"""安装 pre-commit 钩子"""
run_shell_command(["pre-commit", "install", "--install-hooks"])


async def get_labels(event: PullRequestEvent | IssuesEvent):
"""获取议题或拉取请求的标签"""
if isinstance(event, PullRequestClosed | PullRequestReviewSubmitted):
Expand Down Expand Up @@ -186,3 +175,36 @@ async def is_config_workflow(
return False

return CONFIG_LABEL in labels


async def setup_git(
bot: GitHubBot, installation_id: int = Depends(get_installation_id)
):
"""设置 Git 环境"""
# 绕过检查
# https://github.blog/2022-04-18-highlights-from-git-2-36/#stricter-repository-ownership-checks
run_shell_command(["git", "config", "--global", "safe.directory", "*"])

token = (
await bot.rest.apps.async_create_installation_access_token(
installation_id=installation_id
)
).parsed_data.token

# 设置 GitHub App 令牌
# https://docs.github.com/zh/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation
# https://github.com/actions/create-github-app-token/issues/204
run_shell_command(
[
"git",
"config",
"--global",
f"url.https://x-access-token:{token}@github.com/.insteadOf",
"https://github.com/",
]
)


# async def install_pre_commit_hooks():
# """安装 pre-commit 钩子"""
# run_shell_command(["pre-commit", "install", "--install-hooks"])
6 changes: 3 additions & 3 deletions src/plugins/github/plugins/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@

from src.plugins.github.constants import CONFIG_LABEL, TITLE_MAX_LENGTH
from src.plugins.github.depends import (
bypass_git,
get_github_handler,
get_installation_id,
get_issue_handler,
is_bot_triggered_workflow,
is_config_workflow,
setup_git,
)
from src.plugins.github.handlers import GithubHandler, IssueHandler
from src.plugins.github.plugins.publish.render import render_comment
Expand Down Expand Up @@ -62,7 +62,7 @@ async def check_rule(
)


@config_check_matcher.handle(parameterless=[Depends(bypass_git)])
@config_check_matcher.handle(parameterless=[Depends(setup_git)])
async def handle_remove_check(
bot: GitHubBot,
installation_id: int = Depends(get_installation_id),
Expand Down Expand Up @@ -156,7 +156,7 @@ async def review_submitted_rule(
auto_merge_matcher = on_type(PullRequestReviewSubmitted, rule=review_submitted_rule)


@auto_merge_matcher.handle(parameterless=[Depends(bypass_git)])
@auto_merge_matcher.handle(parameterless=[Depends(setup_git)])
async def handle_auto_merge(
bot: GitHubBot,
event: PullRequestReviewSubmitted,
Expand Down
14 changes: 7 additions & 7 deletions src/plugins/github/plugins/publish/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
TITLE_MAX_LENGTH,
)
from src.plugins.github.depends import (
bypass_git,
get_github_handler,
get_installation_id,
get_issue_handler,
get_related_issue_handler,
get_related_issue_number,
is_bot_triggered_workflow,
is_publish_workflow,
setup_git,
)
from src.plugins.github.handlers import GithubHandler, IssueHandler
from src.providers.validation.models import PublishType, ValidationDict
Expand Down Expand Up @@ -77,7 +77,7 @@ async def check_rule(
)


@publish_check_matcher.handle(parameterless=[Depends(bypass_git)])
@publish_check_matcher.handle(parameterless=[Depends(setup_git)])
async def handle_publish_plugin_check(
bot: GitHubBot,
state: T_State,
Expand Down Expand Up @@ -109,7 +109,7 @@ async def handle_publish_plugin_check(
state["validation"] = result


@publish_check_matcher.handle(parameterless=[Depends(bypass_git)])
@publish_check_matcher.handle(parameterless=[Depends(setup_git)])
async def handle_adapter_publish_check(
bot: GitHubBot,
state: T_State,
Expand All @@ -129,7 +129,7 @@ async def handle_adapter_publish_check(
state["validation"] = result


@publish_check_matcher.handle(parameterless=[Depends(bypass_git)])
@publish_check_matcher.handle(parameterless=[Depends(setup_git)])
async def handle_bot_publish_check(
bot: GitHubBot,
state: T_State,
Expand All @@ -149,7 +149,7 @@ async def handle_bot_publish_check(
state["validation"] = result


@publish_check_matcher.handle(parameterless=[Depends(bypass_git)])
@publish_check_matcher.handle(parameterless=[Depends(setup_git)])
async def handle_pull_request_and_update_issue(
bot: GitHubBot,
validation: ValidationDict = Arg(),
Expand Down Expand Up @@ -202,7 +202,7 @@ async def pr_close_rule(
pr_close_matcher = on_type(PullRequestClosed, rule=Rule(pr_close_rule))


@pr_close_matcher.handle(parameterless=[Depends(bypass_git)])
@pr_close_matcher.handle(parameterless=[Depends(setup_git)])
async def handle_pr_close(
event: PullRequestClosed,
bot: GitHubBot,
Expand Down Expand Up @@ -239,7 +239,7 @@ async def review_submitted_rule(
)


@auto_merge_matcher.handle(parameterless=[Depends(bypass_git)])
@auto_merge_matcher.handle(parameterless=[Depends(setup_git)])
async def handle_auto_merge(
bot: GitHubBot,
event: PullRequestReviewSubmitted,
Expand Down
6 changes: 3 additions & 3 deletions src/plugins/github/plugins/remove/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
from src.plugins.github import plugin_config
from src.plugins.github.constants import TITLE_MAX_LENGTH
from src.plugins.github.depends import (
bypass_git,
get_github_handler,
get_installation_id,
get_issue_handler,
get_related_issue_number,
get_type_by_labels_name,
is_bot_triggered_workflow,
is_remove_workflow,
setup_git,
)
from src.plugins.github.handlers import GithubHandler, IssueHandler
from src.plugins.github.typing import IssuesEvent
Expand Down Expand Up @@ -69,7 +69,7 @@ async def check_rule(
)


@remove_check_matcher.handle(parameterless=[Depends(bypass_git)])
@remove_check_matcher.handle(parameterless=[Depends(setup_git)])
async def handle_remove_check(
bot: GitHubBot,
installation_id: int = Depends(get_installation_id),
Expand Down Expand Up @@ -136,7 +136,7 @@ async def review_submitted_rule(
auto_merge_matcher = on_type(PullRequestReviewSubmitted, rule=review_submitted_rule)


@auto_merge_matcher.handle(parameterless=[Depends(bypass_git)])
@auto_merge_matcher.handle(parameterless=[Depends(setup_git)])
async def handle_auto_merge(
bot: GitHubBot,
event: PullRequestReviewSubmitted,
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/github/plugins/resolve/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

from src.plugins.github.constants import PUBLISH_LABEL, REMOVE_LABEL
from src.plugins.github.depends import (
bypass_git,
get_installation_id,
get_related_issue_handler,
get_related_issue_number,
get_type_by_labels_name,
setup_git,
)
from src.plugins.github.handlers import GithubHandler, IssueHandler
from src.plugins.github.plugins.publish.utils import (
Expand Down Expand Up @@ -57,7 +57,7 @@ async def pr_close_rule(
pr_close_matcher = on_type(PullRequestClosed, rule=pr_close_rule, priority=10)


@pr_close_matcher.handle(parameterless=[Depends(bypass_git)])
@pr_close_matcher.handle(parameterless=[Depends(setup_git)])
async def handle_pr_close(
event: PullRequestClosed,
bot: GitHubBot,
Expand Down
69 changes: 40 additions & 29 deletions tests/plugins/github/config/process/test_config_auto_merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
from pytest_mock import MockerFixture

from tests.plugins.github.event import get_mock_event
from tests.plugins.github.utils import get_github_bot
from tests.plugins.github.utils import (
assert_subprocess_run_calls,
get_github_bot,
mock_subprocess_run_with_side_effect,
should_call_apis,
)


def get_issue_labels(labels: list[str]):
Expand All @@ -28,50 +33,56 @@ def get_issue_labels(labels: list[str]):


async def test_config_auto_merge(
app: App, mocker: MockerFixture, mock_installation
app: App, mocker: MockerFixture, mock_installation, mock_installation_token
) -> None:
"""测试审查后自动合并

可直接合并的情况
"""
from src.plugins.github.plugins.config import auto_merge_matcher

mock_subprocess_run = mocker.patch("subprocess.run")
mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker)

async with app.test_matcher() as ctx:
adapter, bot = get_github_bot(ctx)
event = get_mock_event(PullRequestReviewSubmitted)
event.payload.pull_request.labels = get_issue_labels(["Config", "Plugin"])

ctx.should_call_api(
"rest.apps.async_get_repo_installation",
{"owner": "he0119", "repo": "action-test"},
mock_installation,
)
ctx.should_call_api(
"rest.pulls.async_merge",
{
"owner": "he0119",
"repo": "action-test",
"pull_number": 100,
"merge_method": "rebase",
},
True,
should_call_apis(
ctx,
[
{
"api": "rest.apps.async_get_repo_installation",
"result": mock_installation,
},
{
"api": "rest.apps.async_create_installation_access_token",
"result": mock_installation_token,
},
{
"api": "rest.pulls.async_merge",
"result": True,
},
],
[
{"owner": "he0119", "repo": "action-test"},
{"installation_id": mock_installation.parsed_data.id},
{
"owner": "he0119",
"repo": "action-test",
"pull_number": 100,
"merge_method": "rebase",
},
],
)

ctx.receive_event(bot, event)
ctx.should_pass_rule(auto_merge_matcher)

# 测试 git 命令
mock_subprocess_run.assert_has_calls(
[
mocker.call(
["git", "config", "--global", "safe.directory", "*"],
check=True,
capture_output=True,
), # type: ignore
],
any_order=True,
assert_subprocess_run_calls(
mock_subprocess_run,
[["git", "config", "--global", "safe.directory", "*"]],
)


Expand All @@ -82,7 +93,7 @@ async def test_auto_merge_not_remove(app: App, mocker: MockerFixture) -> None:
"""
from src.plugins.github.plugins.config import auto_merge_matcher

mock_subprocess_run = mocker.patch("subprocess.run")
mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker)

async with app.test_matcher() as ctx:
adapter, bot = get_github_bot(ctx)
Expand All @@ -103,7 +114,7 @@ async def test_auto_merge_not_member(app: App, mocker: MockerFixture) -> None:
"""
from src.plugins.github.plugins.config import auto_merge_matcher

mock_subprocess_run = mocker.patch("subprocess.run")
mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker)

async with app.test_matcher() as ctx:
adapter, bot = get_github_bot(ctx)
Expand All @@ -125,7 +136,7 @@ async def test_auto_merge_not_approve(app: App, mocker: MockerFixture) -> None:
"""
from src.plugins.github.plugins.config import auto_merge_matcher

mock_subprocess_run = mocker.patch("subprocess.run")
mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker)

async with app.test_matcher() as ctx:
adapter, bot = get_github_bot(ctx)
Expand Down
13 changes: 13 additions & 0 deletions tests/plugins/github/config/process/test_config_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ async def test_process_config_check(
mocked_api: MockRouter,
tmp_path: Path,
mock_installation,
mock_installation_token,
mock_results: dict[str, Path],
) -> None:
"""测试发布检查不通过"""
Expand Down Expand Up @@ -118,6 +119,10 @@ async def test_process_config_check(
"api": "rest.apps.async_get_repo_installation",
"result": mock_installation,
},
{
"api": "rest.apps.async_create_installation_access_token",
"result": mock_installation_token,
},
{
"api": "rest.issues.async_get",
"result": mock_issues_resp,
Expand Down Expand Up @@ -155,6 +160,7 @@ async def test_process_config_check(
# 对应的 API 数据
api_data = [
{"owner": "he0119", "repo": "action-test"},
{"installation_id": mock_installation.parsed_data.id},
{"owner": "he0119", "repo": "action-test", "issue_number": 80},
snapshot(
{
Expand Down Expand Up @@ -282,6 +288,13 @@ async def test_process_config_check(
mock_subprocess_run,
[
["git", "config", "--global", "safe.directory", "*"],
[
"git",
"config",
"--global",
"url.https://x-access-token:[email protected]/.insteadOf",
"https://github.com/",
],
["git", "fetch", "origin", "results"],
["git", "checkout", "results"],
["git", "switch", "-C", "config/issue80"],
Expand Down
Loading
Loading