安装配置
安装
- 二进制文件安装https://www.sonarsource.com/products/sonarqube/downloads/
- 建议直接使用
Docker
运行, 省心(注意挂载目录) https://docs.sonarsource.com/sonarqube/latest/setup-and-upgrade/install-the-server/installing-sonarqube-from-docker/ - 安装插件Chinese Pack(可选)
配置Gitlab登录
-
文档说明: https://docs.sonarsource.com/sonarqube/10.1/instance-administration/authentication/gitlab/
- 创建应用
https://<Your Gitlab URL>/-/profile/applications
(read_user权限) - 注意生成callback是
<Your SonarQube URL>/oauth2/callback/gitlab
- 创建应用
-
再次访问登录页出现
Gitlab
登录按钮代表配置成功- 这里注意一定要配置Server base URL否则无法跳转成功
- 配置
Gitlab
导入项目 - 创建Token
https://<Your Gitlab URL>/-/profile/personal_access_tokens
- Gitlab网址为
https://<Your Gitlab URL>/api/v4
- 配置成功之后,在创建项目的地方就会出现Gitlab选项
-
配置多分支(默认只能一个分支)
- 使用插件: https://github.com/mc1arke/sonarqube-community-branch-plugin
- 注意文档中不同的SonarQube版本对应不同版本插件
- 然后还需要修改配置文件
项目配置
- 在SonarQube创建好项目需要准备三个配置(变量名自定义)
- SONAR_HOST_URL=(sonar的访问网址)
- SONAR_PROJECT_KEY=(项目标识)
- SONAR_TOKEN=(令牌, 创建完项目最后一步会给出创建页面)
- 把以上三个配置到Gitlab
.gitlab-ci.yml
sonarqube-check:
stage: check
## 需要 runner 是 Docker 类型才可以, 当下不好用, 以下配置多余的忽略
image:
name: sonarsource/sonar-scanner-cli:latest
entrypoint: [""]
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
GIT_DEPTH: "0"
## 注意引入三个变量
SONAR_HOST_URL: "${SONAR_HOST_URL}"
SONAR_TOKEN: "${SONAR_TOKEN}"
SONAR_PROJECT_KEY: "${SONAR_PROJECT_KEY}"
allow_failure: false
only:
- main
- dev
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
coverage: '/total:\s+\(statements\)\s+\d+.\d+%/'
script:
- bash ./deployment/sonarqube-check.sh
- sonar-scanner -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.token=$SONAR_TOKEN -Dsonar.projectKey=$SONAR_PROJECT_KEY -Dsonar.branch.name=$CI_COMMIT_REF_NAME
.golangci.yml
run:
# 并发运行线程数
concurrency: 4
# 分析超时时间
timeout: 5m
# 退出状态码
issues-exit-code: 1
# 包含测试文件
tests: true
# 忽略以下默认目录
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
output:
# 输出的格式
# colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions
# 打印有问题的代码行
print-issued-lines: true
# 打印没通过哪个检测器
print-linter-name: true
# 问题唯一输出
uniq-by-line: true
# 按文件路径排序
sort-results: false
linters:
disable-all: true # 关闭其他linter
enable: # 下面是开启的linter列表,之后的英文注释介绍了相应linter的功能
# 降低代码复杂度
- cyclop
- gocognit
- gocyclo
- maintidx
# 高可拓展性的go源码linter
- gocritic
# 禁止保留未使用的代码块
#---------------------------------------
- ineffassign
- durationcheck
# 所有err都要处理
- errcheck
# 在Go 1.13之后使用errors.Wrap可能导致的问题
- errorlint
# 检查switch的全面性,以免遗漏场景
- exhaustive
# 禁止将for-range value的指针暴露给外部
- exportloopref
# 禁止出现长函数
- funlen
# 如果有相同的string变量请使用consts替换
- goconst
# 禁止出现长语句
- lll
# 返回两个参数,一个数据,一个是err,禁止两个都是nil
- nilnil
# 如果知道slice大小,定义时需分配空间
- prealloc
# 检查prometheus meteics的指标名是否规范
- promlinter
# 强制要求const/import/var在一个组
- grouper
# 检查变量名长度
- varnamelen
# 强制一致性的impotr别名
- importas
# 类型断言时需检查是否成功
- forcetypeassert
# 保证类型、常量、变量和函数的声明顺序和数量
- decorder
# 检查err的定义规范--types类型的定义是以Error结尾的,常量的定义是Err打头的
- errname
# 禁止errors使用'=='和'!='等表达式--与nil和io.EOF比较除外
- err113
# 官方代码格式化
- gofmt
- goimports
# 检查依赖的黑白名单
- gomodguard
# 检查类似printf的函数是否以f结尾
- goprintffuncname
# 官方错误检查
- govet
# 检查拼写错误
- misspell
# 如果函数过长,禁用裸返回
- nakedret
# 禁止深度嵌套的if语句
- nestif
# 禁止使用Go关键字命名
- predeclared
# 去掉没有必要的type转换
- unconvert
# 检查帮助函数里面有没有调用t.Helper()函数
- thelper
linters-settings:
cyclop:
# 函数最大复杂度=(函数本身是1 + 1 * ('if', 'for', 'case', 'select', '&&', '||'))
# 1 - 10 程序简单,风险小
# 11 - 20 更复杂,中等风险
# 21 - 50 复杂、高风险
# 50 不可测试的代码,非常高的风险
max-complexity: 30
# 跳过测试
skip-tests: false
gocognit:
# 代码复杂度
min-complexity: 30
gocyclo:
# 代码复杂度
min-complexity: 30
dogsled:
# val, _, _ := xx() 这种的 _ 不能超过 n 个
max-blank-identifiers: 2
errcheck:
# `a := b.(MyStruct)`; 类型断言不加 err
check-type-assertions: true
# `num, _ := strconv.Atoi(numStr)`; 忽略错误
check-blank: true
# 要忽略检测的函数列表
# see https://github.com/kisielk/errcheck#excluding-functions for details
exclude-functions:
- io.Copy(*bytes.Buffer)
- io.Copy(os.Stdout)
errorlint:
# 打印错误只能 fmt.Errorf("oh noes: %w", err), 而不是 fmt.Errorf("oh noes: %v", err)
errorf: true
# 使用 ok := errors.As(err, &me), 而不是 myErr, ok := err.(*MyError)
asserts: true
# 使用 errors.Is(err, ErrFoo), 而不是 err == ErrFoo
comparison: true
exhaustive:
# 枚举检查, 枚举在 switch 中不能写 default
default-signifies-exhaustive: false
funlen:
# 函数不能超过 100 行
lines: 100
# 40 个语句 (statements + 空白行 < lines)
statements: 40
goconst:
# 字符串常量最小长度
min-len: 3
# 当这个字符串超过 n 次, 就应该替换为常量
min-occurrences: 3
# 忽略测试
ignore-tests: false
# 查找有现有常量定义的字符串
match-constant: true
# 数字常量也要定义
numbers: true
# 数字的最小值
min: 3
# 数字的最大值
max: 99999
# 当行数不用坐参数时,是否忽略(不忽略, 当 if 的时候也要判断)
ignore-calls: false
gofmt:
# 代码格式化
simplify: true
goimports:
# 优化 import 引入
lll:
# 每一行最大长度
line-length: 120
# tab 宽度=1
tab-width: 1
makezero:
# 只允许初始化长度为0的切片。非零长度的切片 append
always: false
nestif:
# 深度嵌套 if, 最大只能 n
min-complexity: 4
nilnil:
# 不能同时返回两个 nil
checked-types:
- ptr
- func
- iface
- map
- chan
nlreturn:
# return 之前有一个空行
block-size: 1
varnamelen:
max-distance: 9
min-name-length: 1
# prealloc:
# # 优化与分配, 不要项目刚开始的时候使用
# simple: true
# range-loops: true
# for-loops: false
predeclared:
# 声明的东西不能是预订的名字, 比如 new,int
ignore: ""
# include method names and field names (i.e., qualified names) in checks
q: false
staticcheck:
# 静态检测
checks: [ "all" ]
stylecheck:
# 代码风格检测
# https://staticcheck.io/docs/options#checks
checks: [ "all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022" ]
# https://staticcheck.io/docs/options#dot_import_whitelist
dot-import-whitelist:
- fmt
# https://staticcheck.io/docs/options#initialisms
initialisms: [ "ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS" ]
# https://staticcheck.io/docs/options#http_status_code_whitelist
http-status-code-whitelist: [ "200", "400", "404", "500" ]
unparam:
# 没使用的参数
check-exported: false
unused:
exported-is-used: false
exported-fields-are-used: false
./deployment/sonarqube-check.sh
#!/bin/bash
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
go mod tidy
## 代码质量检查
golangci-lint run ./... --out-format checkstyle > report.xml
ret01=$?
if [ $ret01 -ne 0 ]; then
echo -e "\033[31m >>>> 代码质量检查失败,退出 <<<< \033[0m"
exit 1
fi
## 代码覆盖率
go test ./... -coverprofile="coverage.cov" -covermode count
ret02=$?
if [ $ret02 -ne 0 ]; then
echo -e "\033[31m >>>> 代码覆盖率检查失败,退出 <<<< \033[0m"
exit 2
fi
## 给 gitlab 正则匹配找到覆盖率
go tool cover -func="coverage.cov"
## 代码测试
go test -json ./... > report.json
ret03=$?
if [ $ret03 -ne 0 ]; then
echo -e "\033[31m >>>> 代码测试失败,退出 <<<< \033[0m"
exit 3
fi
exit 0
最后结果
- 一切准备就绪之后, 就可以在你的代码仓库显示统计数据