From 7979662086e5d6fa201ded9cf7909ada2fd6412a Mon Sep 17 00:00:00 2001 From: zhaoyingbo Date: Sat, 9 Jul 2022 18:41:28 +0800 Subject: [PATCH] =?UTF-8?q?update:=20=E5=AE=8C=E5=96=84=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devDocker.sh | 1 + main.go | 215 +++++++++++++++++++++++++++++++++++++-------------- model.go | 2 + path.dio | 78 +++++++++++++++++++ 4 files changed, 240 insertions(+), 56 deletions(-) create mode 100644 devDocker.sh create mode 100644 path.dio diff --git a/devDocker.sh b/devDocker.sh new file mode 100644 index 0000000..c4b93a9 --- /dev/null +++ b/devDocker.sh @@ -0,0 +1 @@ +docker run -it --rm --name $(basename `pwd`) -v `pwd`:`pwd` -w `pwd` -p 3001:3001 -v ~/.ssh:/root/.ssh golang:1.18.2 /bin/bash diff --git a/main.go b/main.go index e79d74b..175198a 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "strings" "sync" "time" + "regexp" ) const ( @@ -60,12 +61,136 @@ func init() { }() } +func getBranch(str string) (string) { + r, _ := regexp.Compile("refs/heads/(.*)") + matchArr := r.FindStringSubmatch(str) + if len(matchArr) > 0 { + return matchArr[len(matchArr)-1] + } + return "" +} + +func runCommand(script string, projectPath string, isFile bool) (error) { + log.Printf("Running script: %s\n", script) + var cmd *exec.Cmd + if (isFile) { + cmd = exec.Command(script) + } else { + cmd = exec.Command("/bin/bash", "-c", script) + } + cmd.Stdout = logFile + cmd.Stderr = logFile + cmd.Dir = projectPath + err := cmd.Run() + return err +} + +func manageProj(projectPath string, payload GogsPayload) (error) { + // 如果没有这个文件夹就现clone下来 + if _, err := os.Stat(projectPath); os.IsNotExist(err) { + log.Printf("%s is not exist, start clone", projectPath) + // 创建文件夹 + os.Mkdir(projectPath, os.ModePerm) + // 给文件夹权限 + os.Chmod(projectPath, os.ModePerm) + // clone 到指定文件夹 + script := "git clone " + payload.Repository.SSHURL + " " + projectPath + runErr := runCommand(script, projectPath, false) + if runErr != nil { + return runErr + } + } + // 切换到指定的分支 + branch := getBranch(payload.Ref) + script := "git checkout " + branch + runErr := runCommand(script, projectPath, false) + if runErr != nil { + return runErr + } + // 拉取代码 + script = "git pull" + runErr = runCommand(script, projectPath, false) + if runErr != nil { + return runErr + } + // 切换到指定的commit + script = "git reset --hard " + payload.After + runErr = runCommand(script, projectPath, false) + if runErr != nil { + return runErr + } + return nil +} + +func manageDeploy(scriptPath string, projectPath string) (error) { + // 如果用户是Root就给部署文件添加执行权限 + if os.Getenv("HOME") == "/root" { + log.Printf("Cur user is root, chmod u=rwx to %s", scriptPath) + script := "chmod u=rwx " + scriptPath + runErr := runCommand(script, projectPath, false) + if runErr != nil { + return runErr + } + } + // 开始执行脚本 + runErr := runCommand(scriptPath, projectPath, true) + return runErr +} + +func checkNeedDeploy(payload GogsPayload) (Config, bool) { + // 我们这个系统简单点,只要本次提交包含了部署信号,就直接部署最后一个commit + config := Config{ + Repo: "", + Path: home, + Script: "deploy.py", + Signal: "{D}", + Branch: "", + } + + // 找到指定的项目,获取用户配置 + for _, item := range configs { + if item.Repo == payload.Repository.FullName { + config.Repo = item.Repo + if item.Path != "" { + config.Path = item.Path + } + if item.Script != "" { + config.Script = item.Script + } + if item.Signal != "" { + config.Signal = item.Signal + } + if item.Branch != "" { + config.Branch = item.Branch + } + break + } + } + + // 检查是否需要执行部署脚本,优先分支,然后是推送信息 + + // 分支匹配 + if config.Branch != "" && config.Branch == getBranch(payload.Ref) { + return config, true + } + + // 推送信息匹配 + for _, commit := range payload.Commits { + if strings.Contains(commit.Message, config.Signal) { + return config, true + } + } + + return config, false +} + + func main() { addr := flag.String("a", ":3001", "Address to listen on") flag.Parse() http.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { - // 解析Payload + // 获取请求体 reqBody, err := ioutil.ReadAll(r.Body) if err != nil { log.Printf("Error reading request body: %v\n", err) @@ -73,6 +198,7 @@ func main() { return } + // 解析payload var payload GogsPayload err = json.Unmarshal(reqBody, &payload) if err != nil { @@ -81,65 +207,42 @@ func main() { return } - log.Printf("Received: [Name] %s [Sender] %s", payload.Repository.FullName, payload.Sender.FullName) + // 打印代码库名称以及推送者 + log.Printf("Received: [Repository] %s [Sender] %s", payload.Repository.FullName, payload.Sender.Username) - // 寻找包含Signal的commit - for _, commit := range payload.Commits { - // 寻找配置 - c := Config{ - Path: home, - Script: "deploy.py", - Signal: "{D}", - } - for _, config := range configs { - if config.Repo == payload.Repository.FullName { - c.Repo = config.Repo - if config.Path != "" { - c.Path = config.Path - } - if config.Script != "" { - c.Script = config.Script - } - if config.Signal != "" { - c.Signal = config.Signal - } - break - } - } + // 检查是否需要部署 + deployConfig, needDeploy := checkNeedDeploy(payload) - // 包含部署信号 - if strings.Contains(commit.Message, c.Signal) { - log.Printf("Commit: [SHA] %s [Message] %s", commit.ID, commit.Message) - log.Println("Ready to deploy") - - if c.Repo == "" { - log.Println("Using default config") - } - - projectPath := fmt.Sprintf(c.Path+"/%s/", payload.Repository.Name) - scriptPath := projectPath + c.Script - log.Printf("Running script: %s\n", scriptPath) - - // 开始执行脚本 - cmd := exec.Command(scriptPath) - cmd.Stdout = logFile - cmd.Stderr = logFile - cmd.Dir = projectPath - err = cmd.Run() - if err != nil { - log.Printf("Error running deploy script: %v\n", err) - w.Write([]byte(fmt.Sprintf("Deploy script error: %s", err))) - } else { - log.Print("Deploy finished\n\n") - w.Write([]byte("Deploy finished")) - } - - return - } + // 不需要部署就直接返回 + if (!needDeploy) { + log.Print("No deploy commit found\n\n") + w.Write([]byte("No deploy commit found. Skip.")) + return } - log.Print("No deploy commit found\n\n") - w.Write([]byte("No deploy commit found. Skip.")) + // 准备部署,生成项目地址和脚本地址 + projectPath := fmt.Sprintf(deployConfig.Path+"/%s/", payload.Repository.Name) + scriptPath := projectPath + deployConfig.Script + + // 处理项目,clone并切换到指定的分支和commit + err = manageProj(projectPath, payload) + if err != nil { + log.Printf("Error running manageProj script: %v\n", err) + w.Write([]byte(fmt.Sprintf("manageProj script error: %s", err))) + return + } + + // 给脚本添加权限并执行脚本 + err = manageDeploy(scriptPath, projectPath) + if err != nil { + log.Printf("Error running deploy script: %v\n", err) + w.Write([]byte(fmt.Sprintf("Deploy script error: %s", err))) + } else { + log.Print("Deploy finished\n\n") + w.Write([]byte("Deploy finished")) + } + + return }) log.Println("Webhook service started at " + *addr) diff --git a/model.go b/model.go index 47edc59..b798a2a 100644 --- a/model.go +++ b/model.go @@ -9,6 +9,8 @@ type Config struct { Script string `json:"script"` // 信号:commit messgae里包含信号,则执行脚本 Signal string `json:"signal"` + // 分支 指定分支执行脚本 + Branch string `json:"branch"` } type GogsPayload struct { diff --git a/path.dio b/path.dio new file mode 100644 index 0000000..e3222a6 --- /dev/null +++ b/path.dio @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file