fix bug
All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 1m39s
All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 1m39s
This commit is contained in:
parent
708021c443
commit
25c9b2d18e
@ -1,9 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
Gitea API 客户端
|
Gitea API 客户端
|
||||||
"""
|
"""
|
||||||
|
import re
|
||||||
import httpx
|
import httpx
|
||||||
import os
|
import os
|
||||||
from typing import Tuple
|
from typing import Tuple, Optional
|
||||||
|
|
||||||
|
|
||||||
class GiteaClient:
|
class GiteaClient:
|
||||||
@ -12,7 +13,7 @@ class GiteaClient:
|
|||||||
def __init__(self, gitea_url: str = None, token: str = None):
|
def __init__(self, gitea_url: str = None, token: str = None):
|
||||||
self.gitea_url = (gitea_url or os.getenv("GITEA_URL", "")).rstrip("/")
|
self.gitea_url = (gitea_url or os.getenv("GITEA_URL", "")).rstrip("/")
|
||||||
self.token = token or os.getenv("GITEA_TOKEN", "")
|
self.token = token or os.getenv("GITEA_TOKEN", "")
|
||||||
self.base_api_url = f"{self.gitea_url}/api/v1"
|
self.base_api_url = f"{self.gitea_url}/api/v1" if self.gitea_url else ""
|
||||||
self.client = httpx.Client(timeout=30)
|
self.client = httpx.Client(timeout=30)
|
||||||
|
|
||||||
def _headers(self) -> dict:
|
def _headers(self) -> dict:
|
||||||
@ -22,21 +23,31 @@ class GiteaClient:
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
def _extract_owner_repo_from_pr_url(self, pr_url: str) -> Tuple[str, str, int]:
|
def _parse_pr_url(self, pr_url: str) -> Tuple[str, str, str, int]:
|
||||||
"""
|
"""
|
||||||
从 PR URL 提取 owner, repo, pr_number
|
从 PR URL 提取 base_url, owner, repo, pr_number
|
||||||
例如: https://gitea.airlabs.art/owner/repo/pulls/45
|
例如: https://gitea.airlabs.art/owner/repo/pulls/45
|
||||||
|
返回: ("https://gitea.airlabs.art", "owner", "repo", 45)
|
||||||
"""
|
"""
|
||||||
import re
|
match = re.search(r'(https?://[^/]+)/([^/]+)/([^/]+)/pulls/(\d+)', pr_url)
|
||||||
match = re.search(r'([^/]+)/([^/]+)/pulls/(\d+)', pr_url)
|
|
||||||
if not match:
|
if not match:
|
||||||
raise ValueError(f"无法解析 PR URL: {pr_url}")
|
raise ValueError(f"无法解析 PR URL: {pr_url}")
|
||||||
|
|
||||||
owner, repo, pr_number = match.groups()
|
base_url, owner, repo, pr_number = match.groups()
|
||||||
return owner, repo, int(pr_number)
|
return base_url, owner, repo, int(pr_number)
|
||||||
|
|
||||||
|
def _get_api_url(self, pr_url: Optional[str] = None) -> str:
|
||||||
|
"""获取 API base URL,优先从 pr_url 解析,否则用 self.base_api_url"""
|
||||||
|
if pr_url:
|
||||||
|
match = re.search(r'(https?://[^/]+)/', pr_url)
|
||||||
|
if match:
|
||||||
|
return f"{match.group(1)}/api/v1"
|
||||||
|
if self.base_api_url:
|
||||||
|
return self.base_api_url
|
||||||
|
raise ValueError("无法确定 Gitea API 地址:未配置 GITEA_URL 且 PR URL 中无法提取")
|
||||||
|
|
||||||
def merge_pr(
|
def merge_pr(
|
||||||
self, owner: str, repo: str, pr_number: int
|
self, owner: str, repo: str, pr_number: int, api_base_url: str = None
|
||||||
) -> Tuple[bool, str]:
|
) -> Tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
合并 PR
|
合并 PR
|
||||||
@ -45,11 +56,13 @@ class GiteaClient:
|
|||||||
owner: 仓库所有者
|
owner: 仓库所有者
|
||||||
repo: 仓库名称
|
repo: 仓库名称
|
||||||
pr_number: PR 编号
|
pr_number: PR 编号
|
||||||
|
api_base_url: 可选,API base URL(从 pr_url 解析得到)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(是否成功, 消息)
|
(是否成功, 消息)
|
||||||
"""
|
"""
|
||||||
url = f"{self.base_api_url}/repos/{owner}/{repo}/pulls/{pr_number}/merge"
|
base = api_base_url or self.base_api_url
|
||||||
|
url = f"{base}/repos/{owner}/{repo}/pulls/{pr_number}/merge"
|
||||||
payload = {
|
payload = {
|
||||||
"Do": "merge", # merge / squash / rebase
|
"Do": "merge", # merge / squash / rebase
|
||||||
"MergeMessageField": f"Merge PR #{pr_number} (approved via Log Center)",
|
"MergeMessageField": f"Merge PR #{pr_number} (approved via Log Center)",
|
||||||
@ -75,7 +88,7 @@ class GiteaClient:
|
|||||||
return False, str(e)
|
return False, str(e)
|
||||||
|
|
||||||
def close_pr(
|
def close_pr(
|
||||||
self, owner: str, repo: str, pr_number: int, reason: str = ""
|
self, owner: str, repo: str, pr_number: int, reason: str = "", api_base_url: str = None
|
||||||
) -> Tuple[bool, str]:
|
) -> Tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
关闭 PR(可选添加评论说明原因)
|
关闭 PR(可选添加评论说明原因)
|
||||||
@ -85,20 +98,24 @@ class GiteaClient:
|
|||||||
repo: 仓库名称
|
repo: 仓库名称
|
||||||
pr_number: PR 编号
|
pr_number: PR 编号
|
||||||
reason: 关闭原因(将作为评论添加)
|
reason: 关闭原因(将作为评论添加)
|
||||||
|
api_base_url: 可选,API base URL(从 pr_url 解析得到)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(是否成功, 消息)
|
(是否成功, 消息)
|
||||||
"""
|
"""
|
||||||
|
base = api_base_url or self.base_api_url
|
||||||
|
|
||||||
# Step 1: 如果提供了原因,先添加评论
|
# Step 1: 如果提供了原因,先添加评论
|
||||||
if reason:
|
if reason:
|
||||||
comment_success, comment_msg = self.add_pr_comment(
|
comment_success, comment_msg = self.add_pr_comment(
|
||||||
owner, repo, pr_number, f"## ❌ 修复被拒绝\n\n**原因:**\n{reason}"
|
owner, repo, pr_number, f"## ❌ 修复被拒绝\n\n**原因:**\n{reason}",
|
||||||
|
api_base_url=base,
|
||||||
)
|
)
|
||||||
if not comment_success:
|
if not comment_success:
|
||||||
return False, f"添加评论失败: {comment_msg}"
|
return False, f"添加评论失败: {comment_msg}"
|
||||||
|
|
||||||
# Step 2: 关闭 PR
|
# Step 2: 关闭 PR
|
||||||
url = f"{self.base_api_url}/repos/{owner}/{repo}/pulls/{pr_number}"
|
url = f"{base}/repos/{owner}/{repo}/pulls/{pr_number}"
|
||||||
payload = {"state": "closed"}
|
payload = {"state": "closed"}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -111,7 +128,7 @@ class GiteaClient:
|
|||||||
return False, str(e)
|
return False, str(e)
|
||||||
|
|
||||||
def add_pr_comment(
|
def add_pr_comment(
|
||||||
self, owner: str, repo: str, pr_number: int, comment: str
|
self, owner: str, repo: str, pr_number: int, comment: str, api_base_url: str = None
|
||||||
) -> Tuple[bool, str]:
|
) -> Tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
添加 PR 评论
|
添加 PR 评论
|
||||||
@ -121,12 +138,14 @@ class GiteaClient:
|
|||||||
repo: 仓库名称
|
repo: 仓库名称
|
||||||
pr_number: PR 编号
|
pr_number: PR 编号
|
||||||
comment: 评论内容
|
comment: 评论内容
|
||||||
|
api_base_url: 可选,API base URL
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(是否成功, 消息)
|
(是否成功, 消息)
|
||||||
"""
|
"""
|
||||||
|
base = api_base_url or self.base_api_url
|
||||||
# 注意: Gitea 中 PR 和 Issue 共用评论 API
|
# 注意: Gitea 中 PR 和 Issue 共用评论 API
|
||||||
url = f"{self.base_api_url}/repos/{owner}/{repo}/issues/{pr_number}/comments"
|
url = f"{base}/repos/{owner}/{repo}/issues/{pr_number}/comments"
|
||||||
payload = {"body": comment}
|
payload = {"body": comment}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -137,17 +156,19 @@ class GiteaClient:
|
|||||||
return False, str(e)
|
return False, str(e)
|
||||||
|
|
||||||
def merge_pr_by_url(self, pr_url: str) -> Tuple[bool, str]:
|
def merge_pr_by_url(self, pr_url: str) -> Tuple[bool, str]:
|
||||||
"""通过 PR URL 直接合并"""
|
"""通过 PR URL 直接合并(从 URL 自动解析 Gitea 地址)"""
|
||||||
try:
|
try:
|
||||||
owner, repo, pr_number = self._extract_owner_repo_from_pr_url(pr_url)
|
base_url, owner, repo, pr_number = self._parse_pr_url(pr_url)
|
||||||
return self.merge_pr(owner, repo, pr_number)
|
api_base_url = f"{base_url}/api/v1"
|
||||||
|
return self.merge_pr(owner, repo, pr_number, api_base_url=api_base_url)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, str(e)
|
return False, str(e)
|
||||||
|
|
||||||
def close_pr_by_url(self, pr_url: str, reason: str = "") -> Tuple[bool, str]:
|
def close_pr_by_url(self, pr_url: str, reason: str = "") -> Tuple[bool, str]:
|
||||||
"""通过 PR URL 直接关闭"""
|
"""通过 PR URL 直接关闭(从 URL 自动解析 Gitea 地址)"""
|
||||||
try:
|
try:
|
||||||
owner, repo, pr_number = self._extract_owner_repo_from_pr_url(pr_url)
|
base_url, owner, repo, pr_number = self._parse_pr_url(pr_url)
|
||||||
return self.close_pr(owner, repo, pr_number, reason)
|
api_base_url = f"{base_url}/api/v1"
|
||||||
|
return self.close_pr(owner, repo, pr_number, reason, api_base_url=api_base_url)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, str(e)
|
return False, str(e)
|
||||||
|
|||||||
@ -32,6 +32,9 @@ spec:
|
|||||||
value: "log_center"
|
value: "log_center"
|
||||||
- name: DB_PASSWORD
|
- name: DB_PASSWORD
|
||||||
value: "JogNQdtrd3WY8CBCAiYfYEGx"
|
value: "JogNQdtrd3WY8CBCAiYfYEGx"
|
||||||
|
# Gitea Token(URL 从 PR URL 自动解析)
|
||||||
|
- name: GITEA_TOKEN
|
||||||
|
value: "443f7f2f556b4832f90e46df9af3e21ccb06b8a3"
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "128Mi"
|
memory: "128Mi"
|
||||||
|
|||||||
@ -38,6 +38,7 @@ class ClaudeService:
|
|||||||
cmd = [
|
cmd = [
|
||||||
self.cli_path,
|
self.cli_path,
|
||||||
"-p", prompt,
|
"-p", prompt,
|
||||||
|
"--model", settings.claude_model,
|
||||||
"--dangerously-skip-permissions",
|
"--dangerously-skip-permissions",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ class Settings(BaseSettings):
|
|||||||
# Claude CLI
|
# Claude CLI
|
||||||
claude_cli_path: str = Field(default="claude", description="Claude CLI 路径")
|
claude_cli_path: str = Field(default="claude", description="Claude CLI 路径")
|
||||||
claude_timeout: int = Field(default=1000, description="Claude CLI 超时时间(秒)")
|
claude_timeout: int = Field(default=1000, description="Claude CLI 超时时间(秒)")
|
||||||
|
claude_model: str = Field(default="claude-opus-4-6", description="Claude 模型")
|
||||||
|
|
||||||
# Git
|
# Git
|
||||||
git_user_name: str = Field(default="repair-agent", description="Git 用户名")
|
git_user_name: str = Field(default="repair-agent", description="Git 用户名")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user