「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战」
引子
"kovogo哥, 我怎么提交不了代码了"
没想到工作两年就已经进入哥字辈了, 往旁边一看旁边新来的小伙子正常对着屏幕发愁, 一看控制台的报错,哦原来是golangci-lint
打回了提交, 原因是某一行的代码太长了超过了限制。
那么在提交代码对代码规范进行检查是怎么做到的? 这就需要我们来回顾一下githook
的知识了。
githook
什么是githook
?
一般来说只要是上了规模的软件/中间件/开源框架或多或少的会提供一些hook
, 供开发人员使用。而这些hook
的作用就是在这些框架/软件中引入开发人员编写代码, 从而改变软件的表现形式或行为逻辑。
而githook
就是git
是向开发者提供的在git
执行重要的操作引入开发者的代码的功能,比如上文提到在commit
时引入代码检查的功能就是通过githook
实现的。
每个git仓库在初始化的时候, 都会在.git
目录下创建一个hooks
目录,用来存放开发人员自定义的hook
脚本(shell脚本
), 脚本文件名即hook
名。
windows 环境安装git时会自带
git bash
, 因此无需要担心脚本的兼容性
当hook
脚本返回0
时表示放行对应的操作, 返回非0
值表示拒绝此操作。
每个程序在退出的时候都会有返回值, 该返回值通常用来表示程序是否执行成功.
在Linux shell中我们可以通过, $?
来获取上一个程序的返回值.
如上图所示, 我们尝试访问一个不存在的路径, ls
命令的返回值是2.
我们可以在.git/hooks
中看到如下文件:
上图中.sample文件都是git官方提供的参考, 去掉.sample后缀该钩子就会起效, 记住: 文件名即钩子名
如果需要在提交代码时对代码进行检测,我们就需要为pre-commit
钩子编写代码规范检测脚本。
更多详细信息可参见官方文档
golintci-lint
由于golint
已经被谷歌抛弃不再维护了, 我们使用了golangci-lint
对代码规范进行检查。
安装
golangci-lint官网给出的安装方式有两种:
- 二进制安装, 直接下载对应平台的可执行文件
- 如果版本小于
1.16
使用go get
下载源文件并编译安装, 大于1.16
则使用go install
# Go 1.16+
go install github.com/golangci/golangci-lint/cmd/[email protected]
# Go version < 1.16
go get -u github.com/golangci/golangci-lint/cmd/[email protected]
复制代码
以windows平台为例, 我的golang版本是1.16.10
因此我们使用go install
的方式进行安装
使用
使用run
命令即可对目标目录的代码进行检测
golangci-lint run [目录]
复制代码
使用-h
可以查看run
命令的更多帮助信息
golangci-lint run -h
复制代码
值得注意的选项有:
--issues-exit-code
代码检测失败返回的错误码, 默认是1
-c, --config PATH
用于定制代码检测项目的配置文件(yaml
格式)--skip-files
要跳过的文件--skip-dirs
要跳过的目录--enable
要启用的代码检测项目
定制代码检测
上文提到, 我们可以通过--config
来定制代码检测项目的配置, 其格式如下:
linters-settings:
cyclop: # 代码检测项目名通常会对应到某个插件
# 最大的复杂度
max-complexity: 10
# 每个包允许的最大复杂度
package-average: 0.0
# 是否跳过测试文件
skip-tests: false
复制代码
更多信息参考官方文档
githook + golangci-lint
以windows平台为例, 我们新建一个项目
然后新建一个代码文件, 随便写点乱七八糟的代码:
func main() {
for i := 0; i < 1024; i++ {
for j := 0; j < 1024; j++ {
for n := 0; n < 1024; n++ {
fmt.Printf("%d-%d-%s", i, j, n)
}
}
}
}
复制代码
golangci-lint
检测到了Printf
传了错误的参数
我们再往项目中里面加点有"坏味道"的代码, 如下所示:(示例来自cyclop
项目的测试文件)
func T() {
i := 1
if i > 2 {
if i > 2 {
}
if i > 2 {
}
if i > 2 {
}
if i > 2 {
}
} else {
if i > 2 {
}
if i > 2 {
}
if i > 2 {
}
if i > 2 {
}
}
if i > 2 {
}
}
复制代码
再次进行代码检测, 注意此时需要启用圈复杂度检测插件cyclop
如果我们想要让代码通过检测,就需要使用到上文提到的使用配置文件自定义代码检测项目了。
在项目中创建lint.yaml
, 并编写配置项如下
linters-settings:
cyclop:
# 最大的复杂度
max-complexity: 15
# 每个包允许的最大复杂度
package-average: 0.0
# 是否跳过测试文件
skip-tests: false
复制代码
再次运行并指定配置文件, 此时已不再提示圈复杂度过高
pre-commit hook
利用上文的知识,我们的pre-commit hook 脚本的逻辑如下所示:
- 检测是否安装golangci-lint, 如果没有则进行安装
- 运行golangci-lint对代码进行检测,如果检测失败返回1成功则返回0
#!/bin/sh
echo "Start lint code"
if !(golangci-lint.exe --version); then
go install github.com/golangci/golangci-lint/cmd/[email protected]
fi
# 目录名后跟上...表示对该目录进行递归查找
if !(golangci-lint.exe run --enable cyclop --config ./lint.yaml ./...); then
echo "Lint fail!"
exit 1
fi
echo "Lint success"
exit 0
复制代码
尝试对代码提交,运行效果如下图所示