Skip to content

Commit 2f1c206

Browse files
authored
fix: 修复 git 没有权限推送的问题 (#441)
* fix: 修复 git 没有权限推送的问题 * test: 修复测试 * test: 简化测试写法 * chore: 注释掉安装 pre-commit 钩子的函数
1 parent 065d282 commit 2f1c206

29 files changed

+1580
-1078
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/lang/zh-CN/
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- 修复 git 没有权限推送的问题
13+
1014
## [4.4.4] - 2025-08-10
1115

1216
### Added

src/plugins/github/depends/__init__.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,6 @@
2020
from .utils import extract_issue_number_from_ref
2121

2222

23-
async def bypass_git():
24-
"""绕过检查"""
25-
# https://github.blog/2022-04-18-highlights-from-git-2-36/#stricter-repository-ownership-checks
26-
run_shell_command(["git", "config", "--global", "safe.directory", "*"])
27-
28-
29-
async def install_pre_commit_hooks():
30-
"""安装 pre-commit 钩子"""
31-
run_shell_command(["pre-commit", "install", "--install-hooks"])
32-
33-
3423
async def get_labels(event: PullRequestEvent | IssuesEvent):
3524
"""获取议题或拉取请求的标签"""
3625
if isinstance(event, PullRequestClosed | PullRequestReviewSubmitted):
@@ -186,3 +175,36 @@ async def is_config_workflow(
186175
return False
187176

188177
return CONFIG_LABEL in labels
178+
179+
180+
async def setup_git(
181+
bot: GitHubBot, installation_id: int = Depends(get_installation_id)
182+
):
183+
"""设置 Git 环境"""
184+
# 绕过检查
185+
# https://github.blog/2022-04-18-highlights-from-git-2-36/#stricter-repository-ownership-checks
186+
run_shell_command(["git", "config", "--global", "safe.directory", "*"])
187+
188+
token = (
189+
await bot.rest.apps.async_create_installation_access_token(
190+
installation_id=installation_id
191+
)
192+
).parsed_data.token
193+
194+
# 设置 GitHub App 令牌
195+
# https://docs.github.com/zh/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation
196+
# https://github.com/actions/create-github-app-token/issues/204
197+
run_shell_command(
198+
[
199+
"git",
200+
"config",
201+
"--global",
202+
f"url.https://x-access-token:{token}@github.com/.insteadOf",
203+
"https://github.com/",
204+
]
205+
)
206+
207+
208+
# async def install_pre_commit_hooks():
209+
# """安装 pre-commit 钩子"""
210+
# run_shell_command(["pre-commit", "install", "--install-hooks"])

src/plugins/github/plugins/config/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414

1515
from src.plugins.github.constants import CONFIG_LABEL, TITLE_MAX_LENGTH
1616
from src.plugins.github.depends import (
17-
bypass_git,
1817
get_github_handler,
1918
get_installation_id,
2019
get_issue_handler,
2120
is_bot_triggered_workflow,
2221
is_config_workflow,
22+
setup_git,
2323
)
2424
from src.plugins.github.handlers import GithubHandler, IssueHandler
2525
from src.plugins.github.plugins.publish.render import render_comment
@@ -62,7 +62,7 @@ async def check_rule(
6262
)
6363

6464

65-
@config_check_matcher.handle(parameterless=[Depends(bypass_git)])
65+
@config_check_matcher.handle(parameterless=[Depends(setup_git)])
6666
async def handle_remove_check(
6767
bot: GitHubBot,
6868
installation_id: int = Depends(get_installation_id),
@@ -156,7 +156,7 @@ async def review_submitted_rule(
156156
auto_merge_matcher = on_type(PullRequestReviewSubmitted, rule=review_submitted_rule)
157157

158158

159-
@auto_merge_matcher.handle(parameterless=[Depends(bypass_git)])
159+
@auto_merge_matcher.handle(parameterless=[Depends(setup_git)])
160160
async def handle_auto_merge(
161161
bot: GitHubBot,
162162
event: PullRequestReviewSubmitted,

src/plugins/github/plugins/publish/__init__.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919
TITLE_MAX_LENGTH,
2020
)
2121
from src.plugins.github.depends import (
22-
bypass_git,
2322
get_github_handler,
2423
get_installation_id,
2524
get_issue_handler,
2625
get_related_issue_handler,
2726
get_related_issue_number,
2827
is_bot_triggered_workflow,
2928
is_publish_workflow,
29+
setup_git,
3030
)
3131
from src.plugins.github.handlers import GithubHandler, IssueHandler
3232
from src.providers.validation.models import PublishType, ValidationDict
@@ -77,7 +77,7 @@ async def check_rule(
7777
)
7878

7979

80-
@publish_check_matcher.handle(parameterless=[Depends(bypass_git)])
80+
@publish_check_matcher.handle(parameterless=[Depends(setup_git)])
8181
async def handle_publish_plugin_check(
8282
bot: GitHubBot,
8383
state: T_State,
@@ -109,7 +109,7 @@ async def handle_publish_plugin_check(
109109
state["validation"] = result
110110

111111

112-
@publish_check_matcher.handle(parameterless=[Depends(bypass_git)])
112+
@publish_check_matcher.handle(parameterless=[Depends(setup_git)])
113113
async def handle_adapter_publish_check(
114114
bot: GitHubBot,
115115
state: T_State,
@@ -129,7 +129,7 @@ async def handle_adapter_publish_check(
129129
state["validation"] = result
130130

131131

132-
@publish_check_matcher.handle(parameterless=[Depends(bypass_git)])
132+
@publish_check_matcher.handle(parameterless=[Depends(setup_git)])
133133
async def handle_bot_publish_check(
134134
bot: GitHubBot,
135135
state: T_State,
@@ -149,7 +149,7 @@ async def handle_bot_publish_check(
149149
state["validation"] = result
150150

151151

152-
@publish_check_matcher.handle(parameterless=[Depends(bypass_git)])
152+
@publish_check_matcher.handle(parameterless=[Depends(setup_git)])
153153
async def handle_pull_request_and_update_issue(
154154
bot: GitHubBot,
155155
validation: ValidationDict = Arg(),
@@ -202,7 +202,7 @@ async def pr_close_rule(
202202
pr_close_matcher = on_type(PullRequestClosed, rule=Rule(pr_close_rule))
203203

204204

205-
@pr_close_matcher.handle(parameterless=[Depends(bypass_git)])
205+
@pr_close_matcher.handle(parameterless=[Depends(setup_git)])
206206
async def handle_pr_close(
207207
event: PullRequestClosed,
208208
bot: GitHubBot,
@@ -239,7 +239,7 @@ async def review_submitted_rule(
239239
)
240240

241241

242-
@auto_merge_matcher.handle(parameterless=[Depends(bypass_git)])
242+
@auto_merge_matcher.handle(parameterless=[Depends(setup_git)])
243243
async def handle_auto_merge(
244244
bot: GitHubBot,
245245
event: PullRequestReviewSubmitted,

src/plugins/github/plugins/remove/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
from src.plugins.github import plugin_config
1414
from src.plugins.github.constants import TITLE_MAX_LENGTH
1515
from src.plugins.github.depends import (
16-
bypass_git,
1716
get_github_handler,
1817
get_installation_id,
1918
get_issue_handler,
2019
get_related_issue_number,
2120
get_type_by_labels_name,
2221
is_bot_triggered_workflow,
2322
is_remove_workflow,
23+
setup_git,
2424
)
2525
from src.plugins.github.handlers import GithubHandler, IssueHandler
2626
from src.plugins.github.typing import IssuesEvent
@@ -69,7 +69,7 @@ async def check_rule(
6969
)
7070

7171

72-
@remove_check_matcher.handle(parameterless=[Depends(bypass_git)])
72+
@remove_check_matcher.handle(parameterless=[Depends(setup_git)])
7373
async def handle_remove_check(
7474
bot: GitHubBot,
7575
installation_id: int = Depends(get_installation_id),
@@ -136,7 +136,7 @@ async def review_submitted_rule(
136136
auto_merge_matcher = on_type(PullRequestReviewSubmitted, rule=review_submitted_rule)
137137

138138

139-
@auto_merge_matcher.handle(parameterless=[Depends(bypass_git)])
139+
@auto_merge_matcher.handle(parameterless=[Depends(setup_git)])
140140
async def handle_auto_merge(
141141
bot: GitHubBot,
142142
event: PullRequestReviewSubmitted,

src/plugins/github/plugins/resolve/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
from src.plugins.github.constants import PUBLISH_LABEL, REMOVE_LABEL
66
from src.plugins.github.depends import (
7-
bypass_git,
87
get_installation_id,
98
get_related_issue_handler,
109
get_related_issue_number,
1110
get_type_by_labels_name,
11+
setup_git,
1212
)
1313
from src.plugins.github.handlers import GithubHandler, IssueHandler
1414
from src.plugins.github.plugins.publish.utils import (
@@ -57,7 +57,7 @@ async def pr_close_rule(
5757
pr_close_matcher = on_type(PullRequestClosed, rule=pr_close_rule, priority=10)
5858

5959

60-
@pr_close_matcher.handle(parameterless=[Depends(bypass_git)])
60+
@pr_close_matcher.handle(parameterless=[Depends(setup_git)])
6161
async def handle_pr_close(
6262
event: PullRequestClosed,
6363
bot: GitHubBot,

tests/plugins/github/config/process/test_config_auto_merge.py

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
from pytest_mock import MockerFixture
44

55
from tests.plugins.github.event import get_mock_event
6-
from tests.plugins.github.utils import get_github_bot
6+
from tests.plugins.github.utils import (
7+
assert_subprocess_run_calls,
8+
get_github_bot,
9+
mock_subprocess_run_with_side_effect,
10+
should_call_apis,
11+
)
712

813

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

2934

3035
async def test_config_auto_merge(
31-
app: App, mocker: MockerFixture, mock_installation
36+
app: App, mocker: MockerFixture, mock_installation, mock_installation_token
3237
) -> None:
3338
"""测试审查后自动合并
3439
3540
可直接合并的情况
3641
"""
3742
from src.plugins.github.plugins.config import auto_merge_matcher
3843

39-
mock_subprocess_run = mocker.patch("subprocess.run")
44+
mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker)
4045

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

46-
ctx.should_call_api(
47-
"rest.apps.async_get_repo_installation",
48-
{"owner": "he0119", "repo": "action-test"},
49-
mock_installation,
50-
)
51-
ctx.should_call_api(
52-
"rest.pulls.async_merge",
53-
{
54-
"owner": "he0119",
55-
"repo": "action-test",
56-
"pull_number": 100,
57-
"merge_method": "rebase",
58-
},
59-
True,
51+
should_call_apis(
52+
ctx,
53+
[
54+
{
55+
"api": "rest.apps.async_get_repo_installation",
56+
"result": mock_installation,
57+
},
58+
{
59+
"api": "rest.apps.async_create_installation_access_token",
60+
"result": mock_installation_token,
61+
},
62+
{
63+
"api": "rest.pulls.async_merge",
64+
"result": True,
65+
},
66+
],
67+
[
68+
{"owner": "he0119", "repo": "action-test"},
69+
{"installation_id": mock_installation.parsed_data.id},
70+
{
71+
"owner": "he0119",
72+
"repo": "action-test",
73+
"pull_number": 100,
74+
"merge_method": "rebase",
75+
},
76+
],
6077
)
6178

6279
ctx.receive_event(bot, event)
6380
ctx.should_pass_rule(auto_merge_matcher)
6481

6582
# 测试 git 命令
66-
mock_subprocess_run.assert_has_calls(
67-
[
68-
mocker.call(
69-
["git", "config", "--global", "safe.directory", "*"],
70-
check=True,
71-
capture_output=True,
72-
), # type: ignore
73-
],
74-
any_order=True,
83+
assert_subprocess_run_calls(
84+
mock_subprocess_run,
85+
[["git", "config", "--global", "safe.directory", "*"]],
7586
)
7687

7788

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

85-
mock_subprocess_run = mocker.patch("subprocess.run")
96+
mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker)
8697

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

106-
mock_subprocess_run = mocker.patch("subprocess.run")
117+
mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker)
107118

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

128-
mock_subprocess_run = mocker.patch("subprocess.run")
139+
mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker)
129140

130141
async with app.test_matcher() as ctx:
131142
adapter, bot = get_github_bot(ctx)

tests/plugins/github/config/process/test_config_check.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ async def test_process_config_check(
3131
mocked_api: MockRouter,
3232
tmp_path: Path,
3333
mock_installation,
34+
mock_installation_token,
3435
mock_results: dict[str, Path],
3536
) -> None:
3637
"""测试发布检查不通过"""
@@ -118,6 +119,10 @@ async def test_process_config_check(
118119
"api": "rest.apps.async_get_repo_installation",
119120
"result": mock_installation,
120121
},
122+
{
123+
"api": "rest.apps.async_create_installation_access_token",
124+
"result": mock_installation_token,
125+
},
121126
{
122127
"api": "rest.issues.async_get",
123128
"result": mock_issues_resp,
@@ -155,6 +160,7 @@ async def test_process_config_check(
155160
# 对应的 API 数据
156161
api_data = [
157162
{"owner": "he0119", "repo": "action-test"},
163+
{"installation_id": mock_installation.parsed_data.id},
158164
{"owner": "he0119", "repo": "action-test", "issue_number": 80},
159165
snapshot(
160166
{
@@ -282,6 +288,13 @@ async def test_process_config_check(
282288
mock_subprocess_run,
283289
[
284290
["git", "config", "--global", "safe.directory", "*"],
291+
[
292+
"git",
293+
"config",
294+
"--global",
295+
"url.https://x-access-token:[email protected]/.insteadOf",
296+
"https://github.com/",
297+
],
285298
["git", "fetch", "origin", "results"],
286299
["git", "checkout", "results"],
287300
["git", "switch", "-C", "config/issue80"],

0 commit comments

Comments
 (0)