背景与动机
在项目开发流程中,代码质量和团队协作是影响项目发版成功的关键因素之一,随着项目规模的扩大、版本的迭代、团队成员的增加,代码库的维护会变得愈发复杂。为此,开发团队常常通过各种工具来进行自动化代码检查、自动化代码格式化等质量控制措施。Git
的pre-commit hook
就是其中一种方案,它要求在本地代码提交之前执行指定的校验任务,确保提交的代码符合团队代码规范、质量标准。本文探讨的是一个简化版的pre-commit
钩子设计方案与golang
代码实现。
设计
simple-pre-commit
的流程为:
- 在项目中配置
Makefile
文件,定义自动化的任务
比如下面这个例子:
sql
install:
go mod tidy
go install github.com/brenner8023/simple-pre-commit@latest
simple-pre-commit
test:
go test --race -v
pre-commit:
@make test
-
要求团队成员在初次将远程仓库拉取到本地后执行
make install
,make install
任务中包含了simple-pre-commit
的执行,会往本地项目的.git/hooks
中注入pre-commit
-
此后,开发者每次在本地进行提交之前都会触发pre-commit钩子,执行
make pre-commit
,make pre-commit
中具体执行哪些任务由团队制定。
实现细节
首先,需要检查项目配置,检查项目是否存在Makefile
文件,Makefile
文件中是否编写了pre-commit
的配置
go
func CheckConfig() error {
// 读取Makefile文件
file, err := os.Open("Makefile")
if err != nil {
return err
}
// 用于延迟执行一个函数,直到包含该defer语句的函数返回时才执行。
// 通常用于确保在函数执行结束时释放资源
defer file.Close()
// 编译正则表达式
preCommitReg := regexp.MustCompile(`^pre-commit\s*:`)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
// 逐行读取内容
line := scanner.Text()
// 使用表达式进行匹配
if preCommitReg.MatchString(line) {
return nil
}
}
msg := fmt.Sprintf("pre-commit config not found in Makefile")
return errors.New(msg)
}
找到.git
目录
go
func GetGitProjectRoot() (string, error) {
// 从当前工作目录开始查找
startDir, err := os.Getwd()
if err != nil {
return "", err
}
parentDir := ""
for {
gitDir := filepath.Join(startDir, ".git")
// 判断是否有.git的信息
if info, err := os.Stat(gitDir); err == nil && info.IsDir() {
return startDir, nil
}
// 找不到就往上一级找
parentDir = filepath.Dir(startDir)
if startDir == parentDir {
msg := fmt.Sprintf(".git folder not found")
return "", errors.New(msg)
}
startDir = parentDir
}
}
创建pre-commit
文件并往该文件写入内容
go
func SetHook(gitRoot string) error {
hookFile := filepath.Join(gitRoot, ".git", "hooks", "pre-commit")
command := `make pre-commit`
// 创建pre-commit文件,若文件已存在,文件内容会被清空
file, err := os.Create(hookFile)
if err != nil {
return err
}
defer file.Close()
// 写入任务指令
_, err = file.WriteString(command)
if err != nil {
return err
}
err = os.Chmod(hookFile, 0755)
if err != nil {
return err
}
msg := fmt.Sprintf("%s: Successfully set git hooks", APP_NAME)
fmt.Println(msg)
return nil
}
总结
simple-pre-commit
是一个轻量级的Git pre-commit
钩子管理工具,旨在通过简单的Makefile配置,帮助开发团队在本地代码提交前进行自动化检查,读者可以通过本文了解基本的Git hook工作原理,学习到基本的golang代码语法。