1. gitlab中配置静态代码检查
在Go语言中,可以使用一些第三方工具来进行静态代码扫描,常用的工具包括:
- Go vet:Go语言官方提供的工具,用于检查代码中的常见错误和问题;
- oLint:静态分析工具,可以检查代码中的一些不规范的写法和风格;
- GoMetaLinter:Go语言的多工具静态代码分析器,可以集成多种静态分析工具,例如Go vet、GoLint、gofmt、gocyclo等。其中,GoMetaLinter是一个比较强大的静态代码扫描工具,可以集成多个静态分析工具,并且支持多种输出格式和配置选项。
在GitLab中配置静态代码扫描也比较简单,可以使用.gitlab-ci.yml文件来定义CI/CD流程。以下是一个示例:
stages:
- test
- scan
unit-test:
stage: test
script:
- go test -v ./...
static-scan:
stage: scan
image: golangci/golangci-lint:v1.42.1
script:
- golangci-lint run --timeout 5m
上面的示例定义了两个阶段(stage),分别为test和scan。在test阶段中,使用go test命令运行单元测试。在scan阶段中,使用golangci-lint工具运行静态代码扫描,并将输出结果显示在控制台中。
需要注意的是,上面的示例使用了golangci-lint工具来进行静态代码扫描,因此需要在Docker中安装该工具。在image字段中指定需要使用的Docker镜像,可以选择golangci/golangci-lint或者其他适合自己的镜像。
另外,还需要在项目中添加.gitlab-ci.yml文件,并在GitLab的设置中启用CI/CD流程。在GitLab中,可以通过点击项目设置的CI/CD选项,进入CI/CD设置页面,并在其中添加一个Runner。添加完成后,GitLab会在每次代码提交时自动运行CI/CD流程,并进行单元测试和静态代码扫描。
2. 静态检查
可用于 Go 语言代码分析的工具有很多,比如 golint、gofmt、misspell 等,如果一一引用配置,就会比较烦琐,所以通常我们不会单独地使用它们,而是使用 golangci-lint。
官网地址:
golangci-lint 是一个集成工具,它集成了很多静态代码分析工具,便于我们使用。有以下特点:
- 速度非常快:golangci-lint 是基于 gometalinter 开发的,但是平均速度要比 gometalinter 快 5 倍。速度快的原因有三个:可以并行检查代码;可以复用 go build 缓存;会缓存分析结果。
- 可配置:支持 YAML 格式的配置文件,让检查更灵活,更可控。
- IDE 集成:可以集成进多个主流的 IDE,例如 VS Code、GNU Emacs、Sublime Text、Goland 等。
- linter 聚合器:1.41.1 版本集成了 76 个 linter,并且还支持自定义 linter。
- 最小的误报数:调整了所集成 linter 的默认设置,大幅度减少了误报。
- 良好的输出:输出的结果带有颜色、代码行号和 linter 标识,易于查看和定位。
- 当前更新迭代速度很快,不断有新的 linter 被集成。
这里主要采用golangci-lint,采用其中配置文件定义可以参考文档 https://golangci-lint.run/usage/configuration/ 。
- .golangci.yml
- .golangci.yaml
- .golangci.toml
- .golangci.json
参考文档:《Go 静态代码分析工具 golangci-lint》https://blog.csdn.net/wohu1104/article/details/113751501
在.golangci.yml中,主要分为几个部分,run/output/linters-settings/linters/issues/severity这6部分。
# Options for analysis running.
run:
# See the dedicated "run" documentation section.
option: value
# output configuration options
output:
option: value
# All available settings of specific linters.
linters-settings:
option: value
linters:
option: value
issues:
option: value
severity:
option: value
2.1. run模块
# Options for analysis running.
run:
# 默认可用并发CPU数量.
concurrency: 4
# 静态分析超时时间, e.g. 30s, 5m.
# Default: 1m
timeout: 5m
# 至少一个错误出现时
# 退出代码默认为1
issues-exit-code: 2
# 是否包含测试文件
# 默认是true
tests: false
# 所有linters采用的构建标签
# 默认空列表 [].
build-tags:
- mytag
# 要跳过的目录,其中问题不会被报告,可以采用正则方式匹配。
# 默认值为空列表
# 但是缺省dirs将被跳过,而不依赖于该选项的值(参见skip-dirs-use-default)。"/"将被当前操作系统文件路径分隔符取代,以便在Windows上正常工作。
skip-dirs:
- src/external_libs
- autogenerated_by_my_lib
# Default: true
skip-dirs-use-default: false
# 设置不需要检查的go源码文件,支持正则匹配,这里建议包括:_test.go。
# 默认值为空列表
# 但是不需要包含所有自动生成的文件, "/"将被当前操作系统文件路径分隔符取代,以便在Windows上正常工作。
skip-files:
- ".*\\.my\\.go$"
- lib/bad.go
- ^.*\.(pb|y)\.go$
# 定义Go版本限制。#从go1.18开始主要与泛型支持有关。#默认值:使用Go版本。mod文件,回退到env var ' GOVERSION ',回退到1.18
go: '1.19'
2.2. output模块
# output configuration options
output:
# 输出格式 彩打行号|行号|json|tab|检查格式|代码韧性
# Default: colored-line-number
format: json
# Print lines of code with issue.
print-issued-lines: false
# 在问题报告结尾打印linter名称
# Default: true
print-linter-name: false
# Make issues output unique by line.
# Default: true
uniq-by-line: false
# 输出文件的路径前缀
# Default is no prefix.
path-prefix: ""
# 对结果排序
sort-results: true
2.3. linters模块
golangci-lint 的配置比较灵活,比如你可以自定义要启用哪些 linter。golangci-lint 默认启用的 linter,包括这些:
- depguard - 依赖包的校验
- misspell - 拼写错误检查
- gofumpt - 更强格式化检查
- deadcode - 死代码检查
- errcheck - 返回错误是否使用检查
- gosimple - 检查代码是否可以简化
- govet - 代码可疑检查,比如格式化字符串和类型不一致
- ineffassign - 检查是否有未使用的代码
- staticcheck - 静态分析检查
- structcheck - 查找未使用的结构体字段
- typecheck - 类型检查
- unused - 未使用代码检查
- varcheck - 未使用的全局变量和常量检查
linters:
disable-all: true
enable:
- depguard
- typecheck
- goimports
- gofumpt
- govet
- misspell
- ineffassign
- gosimple
- unused
- errcheck
3. 静态检查运行
- 对当前目录及子目录下的所有 Go 文件进行静态代码检查
$ golangci-lint run
#等效于 golangci-lint run ./...
- 对指定的 Go 文件或者指定目录下的 Go 文件进行静态代码检查
$ golangci-lint run dir1 dir2/... dir3/file1.go
这里需要你注意:上述命令不会检查 dir1 下子目录的 Go 文件,如果想递归地检查一个目录,需要在目录后面追加 /... ,例如:dir2/...
- 根据指定配置文件,进行静态代码检查
$ golangci-lint run -c .golangci.yaml ./...
- 运行指定的 linter
可以传入参数 -E/--enable 来使某个 linter 可用,也可以使用 -D/--disable 参数来使某个 linter 不可用。下面的示例仅仅启用了 errcheck linter :
$ golangci-lint run --no-config --disable-all -E errcheck ./...
默认情况下,golangci-lint 会从当前目录一层层往上寻找配置文件名 .golangci.yaml、.golangci.toml、.golangci.json 直到根(/ )目录。如果找到,就以找到的配置文件作为本次运行的配置文件,所以为了防止读取到未知的配置文件,可以用 --no-config 参数使 golangci-lint 不读取任何配置文件。
禁止运行指定的 liner:
如果我们想禁用某些 linter,可以使用 -D 选项。
$ golangci-lint run --no-config -D godot,errcheck
4. golangci-lint模板实例
//.golangci.yaml
# 官方golangci-lint配置 https://json.schemastore.org/golangci-lint.json
# 使用范例: golangci-lint run -c .golangci.yaml ./...
#service:
# use the fixed version to not introduce new linters unexpectedly
# golangci-lint-version: 1.46.2
run:
# timeout for analysis, e.g. 30s, 5m, default is 1m
timeout: 10m
# include test files or not, default is true
tests: false
# 允许跳过目录开关 default is true. Enables skipping of directories:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs-use-default: true
skip-dirs: # 设置要忽略的目录
- util
skip-files: # 设置不需要检查的go源码文件,支持正则匹配,这里建议包括:_test.go
# Skip autogenerated files.
- ^.*\.(pb|y)\.go$
output:
#对结果排序
sort-results: true
# # 输出格式 彩打行号|行号|json|tab|检查格式|代码韧性
# # Default: colored-line-number
# format: json
# # Print lines of code with issue.
# print-issued-lines: false
linters:
disable-all: true
enable:
# TODO by project
- depguard
- typecheck
- gofumpt
- govet
- misspell
- ineffassign
- gosimple
- unused
- errcheck
# - depguard - 依赖包的校验
# - typecheck - 类型检查
# - misspell - 拼写错误检查
# - gofumpt - 更强格式化检查
# - deadcode - 死代码检查
# - errcheck - 返回错误是否使用检查
# - gosimple - 检查代码是否可以简化
# - govet - 代码可疑检查,比如格式化字符串和类型不一致
# - ineffassign - 检查是否有未使用的代码
# - staticcheck - 静态分析检查
# - unused - 未使用代码检查
# - varcheck - 未使用的全局变量和常量检查
#- goimports
linters-settings:
depguard:
list-type: blacklist
include-go-root: true
packages-with-error-message:
# TODO by project
- sync/atomic: "Use go.uber.org/atomic instead of sync/atomic"
- io/ioutil: "Use corresponding 'os' or 'io' functions instead."
- regexp: "Use github.com/grafana/regexp instead of regexp"
- github.com/stretchr/testify/assert: "Use github.com/stretchr/testify/require instead of github.com/stretchr/testify/assert"
# gofumpt:
# extra-rules: true
# lang-version: "1.15" #判断go版本
#goimports:
# local-prefixes: github.com/prometheus/prometheus
issues:
# 同文件中最多问题数量限制,0关闭,默认3
max-same-issues: 0
exclude-rules:
- path: _test.go
linters:
- errcheck