你是否在codereview中花大量时间去做低效率的review?是否拿到代码屎山不知如何开始优化?是否经常上线后才发现一些潜在的低级错误导致事故频繁?那么,是时候接入了,我愿称之为:golang代码业的屎壳郎-golangci-lint
本文将从介绍->安装->基础使用->高级使用进行讲解。
功能介绍
golangci-lint 官网
golangci-lint 是一个用于检查和静态分析 Go 代码的工具,它可以帮助开发人员发现代码中的潜在问题,提高代码质量和可维护性。
提供了一套丰富的静态分析工具和插件,可以帮助开发人员检查Go代码中的各种问题,包括但不限于以下内容:
- 代码风格检查: 检查代码是否符合Go的编码规范,如缩进、命名约定等。
- 代码质量检查:检查代码中的潜在问题,如未使用的变量,未导入的包等。目前很多IDE都是支持该功能。
- 安全漏洞检查:检查代码中是否存在安全漏洞,如潜在的SQL注入、跨站脚本攻击等。
- 并发问题检查:检查代码中的静态条件和数据竞争。如操作map,但未加锁,在并发下是不安全的。
- 性能问题检查,检查代码中的瓶颈和低效操作。如嵌套循环查找特定元素,但实际上只需要遍历列表一次就可以找到,但嵌套会浪费计算资源。
- 错误处理检查,检查代码中是否正确处理了错误,避免潜在的奔溃。如忽略错误的处理,可能会导致panic
只是简单的介绍了下常规的检查,怎么样,是不是感觉为codereview节约了大量低效检查?只需要在性能、架构、实现方式上进行review就好了。强烈建议在代码提交之前跑一遍,慢慢的,代码质量就上去了。
安装方式
- 二进制进行安装,适合Linux和Winodws,官网如下推荐:
# binary will be $(go env GOPATH)/bin/golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.54.2
golangci-lint --version
但是 很多地区的DSN服务器都没有更新到raw.githubusercontent.com 这个条记录,所以也是肯定下载不来的,如何解决呢?
1:查找githubusercontent.com注册的DNS服务器地址,推荐使用阿里云平台:https://whois.aliyun.com/
2:找到对应的DNS服务器,随便一条都可以
3:ping 这个DNS服务器,获取到ip地址
4:写入hosts文件中
5:即可正常的走上面的curl流程
- macOS 直接使用homebrew安装
brew install golangci-lint
brew upgrade golangci-lint
- go get/go install
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
虽然这种方式最轻便,但官方不推荐,给出的理由是:
# 在go get命名上加上 -u标志,会升级依赖项,最新的可能没有经过测试,不能保证正常工作。(这个理由有点勉强,没经过测试你发到master干啥)
# go.mod的替换指令不使用,有的可能需要替换到某个版本。
# 稳定性取决于用户的Go版本,例如取决于Go<=1.12的错误
# 在 Go modules下会遇到hash的问题
# master分支,不能被认为是最稳定的 (这个理由,个人不能接受)
# 它比二进制安装慢 (但是它快啊)
具体怎么安装,各位斟酌,我 还是使用二进制吧...
基础使用
命令和选项
可以通过 golang-lint -h
命令,直接查看它的用法:
E:\workspaces\goland\be-name> golangci-lint -h
Smart, fast linters runner.
Usage:
golangci-lint [flags]
golangci-lint [command]
Available Commands:
cache Cache control and information
completion Generate the autocompletion script for the specified shell
config Config
help Help
linters List current linters configuration
run Run the linters
version Version
Flags:
--color string Use color when printing; can be 'always', 'auto', or 'never' (default "auto")
-j, --concurrency int Concurrency (default NumCPU) (default 8)
--cpu-profile-path string Path to CPU profile output file
-h, --help help for golangci-lint
--mem-profile-path string Path to memory profile output file
--trace-path string Path to trace output file
-v, --verbose verbose output
--version Print version
命令 | 说明 | 子命令 |
---|---|---|
cache | 缓存控制并打印缓存信息 | clean:清除用户缓存出错或者缓存内容过大;status:打印cache相关的信息,如大小和目录 |
completion | 指定的shell生成自动补全脚本 | bash;fish;powershell/zsh等命令 |
config | 打印当前使用的配置文件 | path:当前配置文件的路径 |
help | 相关的帮助信息 | |
linters | 打印支持的linter,分为启用、禁用两大类 | |
run | 常用的,对代码进行检查 | |
version | 查看当前安装的golangci-lint对应的版本 |
安装完成后,进入到项目目录,直接运行
golangci-lint run
然后根据提示修改吧。
高级使用
集成在golang上
建议集成在ide上,这样使用起来特别的方便,代码查找也很方便,下面介绍如何集成在golang上。
- 安装插件,在goland插件市场搜索Go Linter,然后安装
- 在File Wathcers中点击+,选择golangci-lint进行创建,并选择默认配置project即可
启用配置文件
上述,我们可以通过run
命令来进行代码检查,但是对于团队来说,有很多约定,如引入包名的顺序、针对正则的配置等等,这些需要忽略,那每次都需要在run后面添加参数。通过上述的linters命令,可以得知,我们的代码检查可以分为启用、禁用两大类,那就可以使用文件的方式放入代入代码仓库中,会自动取根目录下查找是否有配置文件,然后进行配置的校验,支持下面几种命名:
- .golangci.yml
- .golangci.yaml
- .golangci.toml
- .golangci.json
附上我最近项目中的配置文件内容,具体的配置解释,以及检测后需要更多的解释,请移步 linters
# Ref: https://golangci-lint.run/usage/configuration/
run:
skip-dirs-use-default: true
linters:
enable:
- containedctx
- errname
- gochecknoinits
- goconst
- gocritic
- gosimple
- ineffassign
- makezero
- nolintlint
- nilnil
- nonamedreturns
- nosprintfhostport
- predeclared
- revive
- tenv
- testableexamples
- thelper
- unconvert
- unused
- usestdlibvars
- wastedassign
disable:
- musttag
- errchkjson
- errorlint
- goimports
presets:
- bugs
实际的操作以及如何排错
当我们加入了配置文件,以及run
之后,会有一些错误出来,如:
golangci-lint run
services\common\result\result.go:12:30: commentFormatting: put a space between `//` and comment text (gocritic)
GroupWord string //字的组合
^
^
services\filter\filter.go:25:18: G404: Use of weak random number generator (math/rand instead of crypto/rand) (gosec)
randomIndex := rand.Intn(len(books))
^
^
services\filter\filter.go:55:58: unused-parameter: parameter 'count' seems to be unused, consider removing or renaming it as _ (revive)
func filterNameByBook(book book.Content, surname string, count int) result.Info { ^
services\filter\filter.go:137:3: S1002: should omit comparison to bool constant, can be simplified to `wordInfo.IsManyRead` (gosimple
)
wordInfo.IsManyRead == true ||
^
services\wash\wash.go:30:4: error is not nil (line 28) but it returns nil (nilerr)
return nil
^
services\source\source.go:70:10: indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (r
evive)
} else {
return c
}
services\source\source.go:96:11: var-declaration: should omit type int from declaration of var line; it will be inferred from the rig
ht-hand side (revive)
var line int = 1
^
- 括号中的就是linter检测的具体项,如(gocritic),如果不清楚它规则和如何修改,可以去 linters 查看。
- 一些典型的例子,如:services\source\source.go:70:10 ,在这个模块中,else可以去掉,直接return c即可。
- 有一些不需要检查的,可以告诉golangci-lint不要对该行检查:
nolint
,对整个func则在func上进行忽略检查:
// nolint:all
func resolve(path string, info any) error {
...
}
- 还可以忽略当前文件的检查,则在package上添加
//nolint:unparam
该如何正确的使用golangci-lint
- 新项目应该立即使用
- 存量项目,在新代码上应当使用:
golangci-lint run --new-from-rev=HEAD~1
- 存量项目,应当按模块划分Owner,然后针对于模块进行清理
golangci-lint run services/common/book
services\common\book\book.go:6:37: commentFormatting: put a space between `//` and comment text (gocritic)
Content []Content `json:"context"` //具体内容
^
- 每次提交前,应当自行跑一遍,且按规定修改完后再进行提交
- 看官方文档,可以在github的aciton中去加入golangci-linit,这样如果有问题,则构建失败。对于新项目来说是可以的,但是存量项目,不亚于一次集中式的重构。感兴趣的可以去看看,就第一节就讲了。
- 添加自己的规则,查看自定义linter
参考资料
https://golangci-lint.run/