代码质量管家-使用golangci-lint提高Go项目质量

miloyang
0 评论
/ /
979 阅读
/
9236 字
07 2023-09

你是否在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流程

dnshostdemo

  • 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

然后根据提示修改吧。 golangcidemo

高级使用

集成在golang上

建议集成在ide上,这样使用起来特别的方便,代码查找也很方便,下面介绍如何集成在golang上。

  • 安装插件,在goland插件市场搜索Go Linter,然后安装
    goland_chajian_1
  • 在File Wathcers中点击+,选择golangci-lint进行创建,并选择默认配置project即可
    goland_chajian_2

启用配置文件

上述,我们可以通过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/
人未眠
工作数十年
脚步未曾歇,学习未曾停
乍回首
路程虽丰富,知识未记录
   借此博客,与之共进步