k8s-wk/go/main/main.go
2025-04-27 15:41:01 +08:00

281 lines
7.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// main.go
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"time"
)
// BuildRequest represents a build job in the queue
type BuildRequest struct {
RepoURL string
Branch string
}
// BuildQueue manages the build requests
type BuildQueue struct {
queue []BuildRequest
mu sync.Mutex
}
var buildQueue = &BuildQueue{}
// Add adds a new build request to the queue
func (bq *BuildQueue) Add(req BuildRequest) {
bq.mu.Lock()
defer bq.mu.Unlock()
bq.queue = append(bq.queue, req)
go bq.processQueue() // Start processing if not already running
}
// processQueue handles the build requests one at a time
func (bq *BuildQueue) processQueue() {
bq.mu.Lock()
if len(bq.queue) == 0 {
bq.mu.Unlock()
return
}
// Get the next request
req := bq.queue[0]
queueLength := len(bq.queue) - 1 // 减去当前正在处理的请求
bq.queue = bq.queue[1:]
bq.mu.Unlock()
// 发送准备构建通知
webhookURL := "https://open.feishu.cn/open-apis/bot/v2/hook/8648822c-fbf8-45ba-bd4d-907c2abdb885"
if err := sendFeishuNotification(webhookURL, &req, "", "prepare", queueLength); err != nil {
log.Printf("发送准备构建通知失败: %v", err)
}
// Process the build request
err := processBuild(&req)
if err != nil {
log.Printf("Error processing build: %v", err)
}
// Process next item in queue
go bq.processQueue()
}
func processBuild(req *BuildRequest) error {
log.Printf("开始处理构建请求 - 仓库: %s, 分支: %s", req.RepoURL, req.Branch)
// 发送开始构建通知
webhookURL := "https://open.feishu.cn/open-apis/bot/v2/hook/8648822c-fbf8-45ba-bd4d-907c2abdb885"
if err := sendFeishuNotification(webhookURL, req, "", "start", 0); err != nil {
log.Printf("发送开始构建通知失败: %v", err)
}
// Create a temporary directory for the build
buildDir, err := os.MkdirTemp("", "build-*")
if err != nil {
return fmt.Errorf("创建构建目录失败: %v", err)
}
log.Printf("创建临时构建目录: %s", buildDir)
defer func() {
log.Printf("清理临时构建目录: %s", buildDir)
os.RemoveAll(buildDir)
}()
// Clone the repository
log.Printf("开始克隆仓库...")
cloneCmd := exec.Command("git", "clone", "-b", req.Branch, req.RepoURL, buildDir)
output, err := cloneCmd.CombinedOutput()
if err != nil {
return fmt.Errorf("git clone失败:\n命令输出: %s\n错误: %v", string(output), err)
}
log.Printf("仓库克隆成功")
// 获取版本号
log.Printf("获取版本号...")
cmd := exec.Command("git", "describe", "--tags", "--abbrev=0", "--exact-match", "HEAD")
cmd.Dir = buildDir
version, err := cmd.Output()
if err != nil {
// 如果没有tag则使用commit id
cmd = exec.Command("git", "rev-parse", "--short", "HEAD")
cmd.Dir = buildDir
version, err = cmd.Output()
if err != nil {
return fmt.Errorf("获取commit id失败: %v", err)
}
}
versionStr := strings.TrimSpace(string(version))
log.Printf("版本号: %s", versionStr)
// Run make deploy
log.Printf("开始执行make all命令...")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
makeCmd := exec.CommandContext(ctx, "make")
makeCmd.Dir = buildDir
// 直接设置标准输出和错误输出
makeCmd.Stdout = os.Stdout
makeCmd.Stderr = os.Stderr
log.Printf("正在启动make 命令...")
if err := makeCmd.Start(); err != nil {
return fmt.Errorf("启动make命令失败: %v", err)
}
log.Printf("make all命令已启动开始执行...")
// 等待命令完成
if err := makeCmd.Wait(); err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
return fmt.Errorf("命令执行失败,退出码: %d", exitErr.ExitCode())
}
return fmt.Errorf("make命令执行失败: %v", err)
}
if ctx.Err() == context.DeadlineExceeded {
return fmt.Errorf("命令执行超时")
}
log.Printf("cicd执行完成")
// 发送飞书通知
if err := sendFeishuNotification(webhookURL, req, versionStr, "success", 0); err != nil {
log.Printf("发送飞书通知失败: %v", err)
} else {
log.Printf("飞书通知发送成功")
}
return nil
}
// FeishuMessage 飞书消息结构
type FeishuMessage struct {
MsgType string `json:"msg_type"`
Content struct {
Text string `json:"text"`
} `json:"content"`
}
// sendFeishuNotification 发送飞书通知
func sendFeishuNotification(webhook string, request *BuildRequest, version string, msgType string, queueLength int) error {
var messageText string
switch msgType {
case "prepare":
waitTime := queueLength * 3 // 假设每个构建大约需要3分钟
messageText = fmt.Sprintf("⌛ 构建请求已收到\n"+
"───────────────\n"+
"📦 项目: %s\n"+
"🌿 分支: %s\n"+
"📊 队列状态: 前面还有 %d 个构建请求\n"+
"⏳ 预计等待: 约 %d 分钟\n"+
"───────────────\n"+
"请耐心等待...",
request.RepoURL,
request.Branch,
queueLength,
waitTime)
case "start":
messageText = fmt.Sprintf("🏗️ 开始构建\n"+
"───────────────\n"+
"📦 项目: %s\n"+
"🌿 分支: %s\n"+
"🕐 时间: %s\n"+
"───────────────\n"+
"构建进行中...",
request.RepoURL,
request.Branch,
time.Now().Format("2006-01-02 15:04:05"))
case "success":
messageText = fmt.Sprintf("🚀 部署通知\n"+
"───────────────\n"+
"📦 项目: %s\n"+
"🌿 分支: %s\n"+
"🏷️ 版本: %s\n"+
"✨ 状态: 部署成功\n"+
"🕐 时间: %s\n"+
"───────────────\n"+
"祝您工作愉快!👨‍💻",
request.RepoURL,
request.Branch,
version,
time.Now().Format("2006-01-02 15:04:05"))
}
message := FeishuMessage{
MsgType: "text",
Content: struct {
Text string `json:"text"`
}{
Text: messageText,
},
}
jsonData, err := json.Marshal(message)
if err != nil {
return fmt.Errorf("序列化消息失败: %v", err)
}
resp, err := http.Post(webhook, "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("发送通知失败: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("飞书API返回错误: status=%d, body=%s", resp.StatusCode, string(body))
}
return nil
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var payload struct {
Repository struct {
GitHTTPURL string `json:"git_http_url"`
} `json:"repository"`
Ref string `json:"ref"`
}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, "Failed to parse webhook payload", http.StatusBadRequest)
return
}
// Extract branch name from ref (refs/heads/master -> master)
branch := filepath.Base(payload.Ref)
// Add build request to queue
buildQueue.Add(BuildRequest{
RepoURL: payload.Repository.GitHTTPURL,
Branch: branch,
})
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Build request queued")
}
func main() {
http.HandleFunc("/webhook", webhookHandler)
port := "29999"
log.Printf("Starting server on port %s", port)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatal(err)
}
}