目录

GO语言开发实践-commit-msg钩子校验提交信息

当我们需要对 Git 提交的相关内容做校验时,可以使用客户端的 commit-msg 钩子。

commit-msg 钩子

前文我们使用 pre-receiveGitlab 服务器做 Commit 提交信息校验,这么做的好处是可以作用于全局,可以让所有的提交都规范,如果我们公司或者组织没有这个要求,就想自己去约束自己呢,那就直接在客户端侧去实现.

  • commit-msg 钩子,读取代码仓库的 commit 信息,在 commit 信息提交的时候,对 commit 信息做校验,如不符合我们定义的规范,则不给提交,并推送告警信息和提示信息。

  • 范围:作用于本地客户端单个项目。

具体详情可以参阅官网:这里

实施步骤

开始前,我们必须跟业务侧约定好 commit 信息的规范,同时设计好钩子的作用范围,本文只针对单个项目起作用。

前提条件

commit message 要规范:

格式:<类型>[可选的作用域]:# <描述>

实现原理

hooks 钩子会收到1个参数,此参数是存有当前提交信息的临时文件的路径。如果该钩子脚本以非零值退出,Git 将放弃提交,因此,可以用来在提交通过前验证项目状态或提交信息。

Git 的 工作流程如下:

/go%E8%AF%AD%E8%A8%80%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5-commit-msg%E9%92%A9%E5%AD%90%E6%A0%A1%E9%AA%8C%E6%8F%90%E4%BA%A4%E4%BF%A1%E6%81%AF/gitlab-commit-workflow.png
Git 流程图
信息
  • 钩子文件位于当前项目目录 .git/hooks 目录下,当 git init 初始化一个新版本库时,Git 默认会在 hooks 目录下放示例脚本

  • 确保钩子文件可执行并且用户为 Git 启动用户的权限。

编码

新建 commit-msg 目录,新建 main.go 文件,复制以下代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package main

import (
    "fmt"
    "os"
    "os/exec"
    "regexp"
    "strings"
)

type CommitType string

const (
    CommitMessagePattern = `((feat|fix|to|docs|style|refactor|perf|test|chore|revert|merge|sync|build|ci)(\()(\s|\S){0,}(\)):(\s)*#(\s|\S)+(\s)(\s|\S)+)|^Merge\ (.*)`
    checkFailedMeassge = `GL-HOOK-ERR:##############################################################################
GL-HOOK-ERR:##                                                                          
GL-HOOK-ERR:## Commit message style check failed!                                       
GL-HOOK-ERR:##                                                                          
GL-HOOK-ERR:## Commit message style must satisfy this regular:                          
GL-HOOK-ERR:##   类型(可选的作用域):#Jira_ID 描述                               
GL-HOOK-ERR:##                                                                          
GL-HOOK-ERR:## Example:                                                                 
GL-HOOK-ERR:##   fix(DAO):#JIRA-001 用户查询缺少username属性                            
GL-HOOK-ERR:##                                                                          
GL-HOOK-ERR:##############################################################################`)

var commitMsgReg = regexp.MustCompile(CommitMessagePattern)

func main() {

    // 获取第一个参数 $1
    param := os.Args[1]

    commitMsg := getCommitMsg(param)
    commitTypes := commitMsgReg.FindAllStringSubmatch(commitMsg, -1)
    if len(commitTypes) != 1 {
        // 打印异常commit message
        commitMessagePrint(commitMsg)
        // 退出并打印规范提示
        checkFailed()
    } else {
        fmt.Println(" ")
    }
}

// 获取git commit 信息,去掉换行符
func getCommitMsg(CommitMessage string) string {
    cmd := fmt.Sprintf("cat %s |head -n 1", CommitMessage)
    getCommitMsgCmd := exec.Command("bash", "-c", cmd)
    getCommitMsgCmd.Stdin = os.Stdin
    getCommitMsgCmd.Stderr = os.Stderr
    b, err := getCommitMsgCmd.Output()
    if err != nil {
        fmt.Print(err)
        os.Exit(1)
    }

    commitMsg := Strip(string(b))
    return commitMsg
}

func checkFailed() {
    fmt.Fprintln(os.Stderr, checkFailedMeassge)
    os.Exit(1)
}

// 打印message信息
func commitMessagePrint(message string) {
    fmt.Fprintln(os.Stderr, "GL-HOOK-ERR:##" + " " + "错误的 commit message: " + message)
}

// 去除换行空格
func Strip(s string) (r string) {
	if s != " " {
		s = strings.Trim(s, " ")
		s = strings.Trim(s, "\t")
		s = strings.Trim(s, "\n")
		r = strings.Trim(s, "\r")
	}
	return
}

使用方法

假设你本地已经装好了 golang,然后设置 go module 模式

如果你是 windows 系统,需要交叉编译得先设置 go env -w GOOS=linux

1
2
3
4
5
6
7
8
cd commit-msg
go env -w CGO_ENABLED=0
go env -w GO111MODULE=on
go env -w GOARCH=amd64
go env -w GOOS=linux
go mod init commit-msg
go mod tidy
go build -ldflags="-w"

把编译后的文件丢进项目的 .git/hooks 目录 并给与可执行权限,即可。