246 lines
6.6 KiB
Go
246 lines
6.6 KiB
Go
package platforms
|
|
|
|
import (
|
|
"code-review/services/types"
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
)
|
|
|
|
// GogsEvent Gogs 平台的 webhook 事件
|
|
type GogsEvent struct {
|
|
apiBase string
|
|
token string
|
|
Event string // 事件类型
|
|
client *httpClient
|
|
Ref string `json:"ref"`
|
|
Before string `json:"before"`
|
|
After string `json:"after"`
|
|
CompareURL string `json:"compare_url"`
|
|
Commits []struct {
|
|
ID string `json:"id"`
|
|
Message string `json:"message"`
|
|
URL string `json:"url"`
|
|
Author Author `json:"author"`
|
|
Committer Author `json:"committer"`
|
|
Timestamp string `json:"timestamp"`
|
|
} `json:"commits"`
|
|
CommitDetail struct {
|
|
Files []struct {
|
|
Filename string `json:"filename"`
|
|
Status string `json:"status"`
|
|
Additions int `json:"additions"`
|
|
Deletions int `json:"deletions"`
|
|
Patch string `json:"patch"`
|
|
} `json:"files"`
|
|
}
|
|
Repository struct {
|
|
ID int `json:"id"`
|
|
Owner Author `json:"owner"`
|
|
Name string `json:"name"`
|
|
FullName string `json:"full_name"`
|
|
Description string `json:"description"`
|
|
Private bool `json:"private"`
|
|
Fork bool `json:"fork"`
|
|
HTMLURL string `json:"html_url"`
|
|
SSHURL string `json:"ssh_url"`
|
|
CloneURL string `json:"clone_url"`
|
|
Website string `json:"website"`
|
|
StarsCount int `json:"stars_count"`
|
|
ForksCount int `json:"forks_count"`
|
|
WatchersCount int `json:"watchers_count"`
|
|
OpenIssuesCount int `json:"open_issues_count"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
CreatedAt string `json:"created_at"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
} `json:"repository"`
|
|
Pusher Author `json:"pusher"`
|
|
Sender Author `json:"sender"`
|
|
}
|
|
|
|
type Author struct {
|
|
ID int `json:"id"`
|
|
Login string `json:"login"`
|
|
FullName string `json:"full_name"`
|
|
Email string `json:"email"`
|
|
AvatarURL string `json:"avatar_url"`
|
|
Username string `json:"username"`
|
|
}
|
|
|
|
// NewGogsEvent 创建 Gogs 事件
|
|
func NewGogsEvent(apiBase, token string, event string) *GogsEvent {
|
|
return &GogsEvent{
|
|
apiBase: apiBase,
|
|
token: token,
|
|
Event: event,
|
|
}
|
|
}
|
|
|
|
func (e *GogsEvent) ExtractChanges() (*types.CodeChanges, error) {
|
|
// 检查是否有提交记录
|
|
if len(e.Commits) == 0 {
|
|
log.Printf("没有提交记录,跳过代码审查")
|
|
return nil, nil
|
|
}
|
|
|
|
// 检查是否跳过代码审查
|
|
for _, commit := range e.Commits {
|
|
if strings.Contains(commit.Message, "[skip codereview]") {
|
|
log.Printf("跳过代码审查: commit=%s", commit.ID)
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
if e.client == nil {
|
|
e.client = newHTTPClient(e.apiBase, e.token)
|
|
log.Printf("初始化 HTTP 客户端: url=%s", e.apiBase)
|
|
}
|
|
|
|
changes := &types.CodeChanges{
|
|
Repository: e.Repository.FullName,
|
|
Branch: e.Ref,
|
|
CommitID: e.After,
|
|
Files: make([]types.FileChange, 0),
|
|
}
|
|
|
|
for _, commit := range e.Commits {
|
|
// 直接获取 diff 内容
|
|
apiPath := fmt.Sprintf("/api/v1/repos/%s/git/commits/%s.diff?access_token=%s", e.Repository.FullName, commit.ID, e.token)
|
|
var diffContent string
|
|
|
|
if err := e.client.get(apiPath, &diffContent); err != nil {
|
|
log.Printf("获取提交详情失败: commit=%s, error=%v", commit.ID, err)
|
|
continue
|
|
}
|
|
|
|
// 去除首尾空白
|
|
diffContent = strings.TrimSpace(diffContent)
|
|
if diffContent == "" {
|
|
log.Printf("提交没有变更内容: commit=%s", commit.ID)
|
|
continue
|
|
}
|
|
|
|
// 解析 diff 内容
|
|
diffBlocks := strings.Split(diffContent, "diff --git ")
|
|
// 去除空块
|
|
for _, block := range diffBlocks {
|
|
if strings.TrimSpace(block) == "" {
|
|
continue
|
|
}
|
|
|
|
// 移除 'diff --git ' 前缀
|
|
block = strings.TrimPrefix(block, "diff --git ")
|
|
|
|
// 解析文件名和变更类型
|
|
lines := strings.Split(block, "\n")
|
|
if len(lines) < 2 {
|
|
continue
|
|
}
|
|
|
|
// 获取文件名
|
|
filename := ""
|
|
status := "modified"
|
|
for _, line := range lines {
|
|
if strings.HasPrefix(line, "--- a/") {
|
|
filename = strings.TrimPrefix(line, "--- a/")
|
|
break
|
|
} else if strings.HasPrefix(line, "+++ b/") {
|
|
filename = strings.TrimPrefix(line, "+++ b/")
|
|
break
|
|
}
|
|
}
|
|
|
|
// 如果没有找到文件名,跳过
|
|
if filename == "" {
|
|
continue
|
|
}
|
|
|
|
// 确定变更类型
|
|
if strings.Contains(block, "new file mode") {
|
|
status = "added"
|
|
} else if strings.Contains(block, "deleted file mode") {
|
|
status = "deleted"
|
|
} else if strings.Contains(block, "rename from") {
|
|
status = "renamed"
|
|
}
|
|
|
|
var content strings.Builder
|
|
content.WriteString(fmt.Sprintf("### 变更说明\n"))
|
|
content.WriteString(fmt.Sprintf("提交信息: %s\n\n", commit.Message))
|
|
|
|
switch status {
|
|
case "added":
|
|
content.WriteString(fmt.Sprintf("新增文件: %s\n\n", filename))
|
|
case "modified":
|
|
content.WriteString(fmt.Sprintf("修改文件: %s\n\n", filename))
|
|
case "deleted":
|
|
content.WriteString(fmt.Sprintf("删除文件: %s\n\n", filename))
|
|
case "renamed":
|
|
content.WriteString(fmt.Sprintf("重命名文件: %s\n\n", filename))
|
|
}
|
|
|
|
content.WriteString("### 变更内容\n")
|
|
content.WriteString("```diff\n")
|
|
content.WriteString(block)
|
|
content.WriteString("\n```\n")
|
|
|
|
changes.Files = append(changes.Files, types.FileChange{
|
|
Path: filename,
|
|
Content: content.String(),
|
|
Type: parseFileType(status),
|
|
})
|
|
}
|
|
}
|
|
|
|
return changes, nil
|
|
}
|
|
|
|
func (e *GogsEvent) PostComments(result *types.ReviewResult) error {
|
|
if e.client == nil {
|
|
e.client = newHTTPClient(e.apiBase, e.token)
|
|
log.Printf("初始化 HTTP 客户端: url=%s", e.apiBase)
|
|
}
|
|
|
|
// 创建 issue 评论
|
|
issueBody := fmt.Sprintf("**代码审查报告**\n\n提交: %s\n\n", e.After)
|
|
for _, comment := range result.Comments {
|
|
issueBody += fmt.Sprintf("### 文件: %s\n\n%s\n\n", comment.Path, comment.Content)
|
|
}
|
|
if result.Summary != "" {
|
|
issueBody += fmt.Sprintf("\n### 总结\n\n%s", result.Summary)
|
|
}
|
|
|
|
// 创建 issue
|
|
createIssuePath := fmt.Sprintf("/api/v1/repos/%s/issues", e.Repository.FullName)
|
|
issueData := map[string]interface{}{
|
|
"title": fmt.Sprintf("代码审查: %s", e.After[:7]),
|
|
"body": issueBody,
|
|
}
|
|
|
|
if err := e.client.post(createIssuePath, issueData); err != nil {
|
|
log.Printf("创建 issue 失败: path=%s, error=%v", createIssuePath, err)
|
|
return fmt.Errorf("创建 issue 失败: %w", err)
|
|
}
|
|
|
|
log.Printf("成功创建 issue: path=%s, commitID=%s", createIssuePath, e.After[:7])
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *GogsEvent) GetPlatform() string {
|
|
return "gogs"
|
|
}
|
|
|
|
func parseFileType(t string) types.ChangeType {
|
|
switch t {
|
|
case "add":
|
|
return types.Added
|
|
case "modify":
|
|
return types.Modified
|
|
case "delete":
|
|
return types.Deleted
|
|
default:
|
|
return types.Modified
|
|
}
|
|
}
|