package main import ( "fmt" "log" "net/http" "os" "github.com/gin-gonic/gin" ) // GitLabPayload 简化版的 GitLab Push Webhook 数据结构 type GitLabPayload struct { Ref string `json:"ref"` After string `json:"after"` Project struct { PathWithNamespace string `json:"path_with_namespace"` } `json:"project"` } func handleWebhook(c *gin.Context) { // 1. 校验 GitLab Secret Token Header clientToken := c.GetHeader("X-Gitlab-Token") if globalConfig.SecretToken != "" && clientToken != globalConfig.SecretToken { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid Secret Token"}) return } // 2. 校验 Event 类型 eventType := c.GetHeader("X-Gitlab-Event") if eventType != "Push Hook" { c.JSON(http.StatusOK, gin.H{"status": "ignored", "reason": "Only Push Hook is handled"}) return } // 3. 解析 Body JSON var payload GitLabPayload if err := c.ShouldBindJSON(&payload); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON Payload"}) return } repoName := payload.Project.PathWithNamespace ref := payload.Ref commitID := payload.After if repoName == "" || ref == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Missing repo or ref in payload"}) return } // 4. 匹配脚本路径 repoConfig, exists := globalConfig.Projects[repoName] if !exists { c.JSON(http.StatusOK, gin.H{"status": "ignored", "message": "No script config for repository " + repoName}) return } scriptPath, exists := repoConfig[ref] if !exists { c.JSON(http.StatusOK, gin.H{"status": "ignored", "message": fmt.Sprintf("No script config for %s on %s", repoName, ref)}) return } // 5. 校验脚本文件是否存在 if _, err := os.Stat(scriptPath); os.IsNotExist(err) { c.JSON(http.StatusInternalServerError, gin.H{"error": "Script file not found: " + scriptPath}) return } // 6. 入队等待 worker 按队列顺序消费 enqueueBuildTask(buildTask{ scriptPath: scriptPath, repoName: repoName, ref: ref, commitID: commitID, }) log.Printf("[QUEUED] 收到 push 入队 [%s] (仓库: %s, 分支: %s, commit: %s)\n", scriptPath, repoName, ref, shortCommit(commitID)) c.JSON(http.StatusOK, gin.H{ "status": "queued", "repository": repoName, "ref": ref, "script": scriptPath, }) }