golangci-lint 结合 git hook 扫查代码实践
序言
golangci-lint
是一个用于 golang
的代码检查工具,可以检查代码的语法、风格、错误、性能和安全等问题。本文将介绍如何使用golangci-lint
结合 git hook
扫查代码实践。
golangci-lint 基本使用
安装
1 |
|
如何选择版本:在 https://golangci-lint.run/product/changelog/ 页面搜索适合的版本,比如搜索
go1.18.10
适配的版本:
在浏览器窗口Ctrl+F
搜索support
找到像go1.19 support
字样,往后退一个版本就是go1.18.10
适配的版本
配置文件
- 格式:使用
.golangci.yml
作为配置文件,详细格式参考官方文档 - 如何指定配置文件:运行代码扫描时,配置文件的位置搜索是从当前目录开始,向上搜索,直到找到配置文件或者找到根目录为止,或者使用
--config
参数指定配置文件位置。
基本命令
- 运行代码扫描:
golangci-lint run
。一般在项目根目录执行,后面还可以指定扫描的文件或目录。 - 输出所有的检查器(linters):
golangci-lint linters
。该命令在检查当前版本的golangci-lint
是否有配置的检查器时很有用。 golangci-lint run
加--fix
参数,可以自动修复一些问题。golangci-lint run
加-v
可以输出详细日志。- 代码中设置绕过检查的方法:在代码中添加注释
//no lint:{linter的名称} {原因(可选)}
, 添加在代码行后面则是当前行绕过,添加在
上方则是当前整个代码块绕过。
集成到 git hook
通过权衡,我们可以在 git commit
时进行代码检查,并且只检查修改过的文件(鉴于单个文件检查结果可能不准确,实际上是检查修改过的文件所在的目录)。
要实现这个功能,我们可以在 .git/hooks/pre-commit
文件中添加以下代码:
1 |
|
该脚本首先获取所有改动的 .go
文件,为避免跨目录的问题,然后按目录分组,逐个目录执行 golangci-lint run
命令。(因为golangci-lint
使用指定目录作为参数的方式不允许同时检查多个不同的目录)。
如果有 lint
错误,exit 1
则阻止提交,反之则通过 exit 0
允许提交。
因为 git hook
文件不能随代码提交到远程仓库,为方便团队开发人员使用,我们可以在项目根目录下创建一个 pre-commit.sh
文件以及编写对应的安装脚本。团队开发成员只需执行该脚本即可将该文件复制到本地仓库的 .git/hooks
目录下,并设置该文件为可执行。(该脚本同时也将golangci-lint
程序的安装集成到脚本开头)。安装脚本内容如下:
- linux 版
1 |
|
- windows版:
1 |
|
主要检查器(linters)了解
以下几个检查器包含了大量的子规则,具体可参考官方文档:https://golangci-lint.run/usage/linters/
进行配置
staticcheck
:静态检查器,用于检查代码中的错误、警告、性能问题、安全漏洞、代码风格问题、代码质量问题。gosimple
:代码简化器,用于检查代码中是否有可以简化的代码。stylecheck
:代码风格检查器,用于检查代码中的风格问题,比如空格、缩进、换行、注释风格等。gosec
:安全漏洞检查器,用于检查代码中是否有安全漏洞。govet
:代码检查器,用于检查代码中的语法错误、类型错误、空接口错误、未使用的变量错误、未使用的函数错误、未使用的包错误。
另外还有以下是单个的检查器, 比如:
ineffassign
# 检查无效赋值maintidx
# 衡量代码可维护性指数,指数越高表示代码越易维护makezero
# 确保切片初始化的长度为 0misspell
# 拼写错误检查nestif
# 检查代码中嵌套的 if 语句是否过多nilerr
# 检查是否在if err != nil 时返回 nil,或者反过来nilnil
# 检查是否在 err == nil,却还要检查返回的数据是否为nil,这种情况应该重新思考接口的设计是否合理nlreturn
# return 语句前面应该空一行,提高代码可读性noctx
# 检查没有使用 context.Context 的 http 请求nolintlint
# 检查 nolint 命令是否进行了原因解释nosprintfhostport
# 禁用Sprintf方法用于生成 host:port 字符串,
因为IPv6情况下会出错,参考:https://github.com/stbenjam/no-sprintf-host-portprealloc
# 初始化 slice 时预先分配空间,减少扩容带来的性能损失predeclared
# 避免变量名与语言中的关键字冲突staticcheck
# 该检查器提供了一组静态检查的规则stylecheck
# 提供代码风格检查建议typecheck
# https://github.com/golangci/golangci-lint/discussions/3759unconvert
# 移除多余的类型转换whitespace
# 检查多余的换行
问题记录
- 为什么不使用
pre-commit
脚本工具?
使用
pre-commit
脚本工具时,全局扫描没问题,但要实现仅检查修改过的文件,添加的配置参数一直不起作用或者出现typechecking error: named files must all be in one directory
。pre-commit
的.pre-commit-config.yaml
内容如下:
1 |
|
由于 --new-from-rev=HEAD
参数不起作用,或者会引起typechecking error
,所以最终放弃使用这种方式。
- 首次运行会比较慢。
- 错误位置打开技巧:使用
IDE
工具的话,可以选中错误日志的文件名:行号:列号
,快捷键Ctrl+Shift+N
触发搜索选中的文件,按Enter
自动打开到错误的位置。 Goland
中可以安装go-lint
插件配置实时扫查(比较耗电脑资源)
参考文档
- golangci-lint 官方文档:https://golangci-lint.run/
- 一个重要的检查器官方文档(包含很多子类):https://staticcheck.dev/
- https://ttys3.dev/blog/staticcheck-the-most-confusing-linter-in-golangci-lint