devops [持续交付实践] 基于 sonarqube 的代码检查平台实现

蒋刚毅 · September 11, 2017 · Last by zealot222 replied at November 22, 2023 · 5099 hits
本帖已被设为精华帖!

前言

公司此前用的一直是的 SonarQube5.1(2015 年版本,为兼容 jdk6 和 jdk7 的项目一直没有升级),最近为了 pipeline 的集成刚刚升级到了最新的 SonarQube6.5 版本。
网上对 SonarQube6 的介绍比较少,这里重点先介绍下 SonarQube6 以后的一些新增特性。
1.代码问题重新分级,将问题分为 bug、漏洞、坏味道;将代码检查结果从可靠性、安全性、可维护性几个角度进行问题分类和风险分级。
2.更丰富的代码检查规则,更友好的问题处理曲线展示,更清晰的质量阈和代码规则定制。
3.支持 webhook 功能,可与 jenkins 等持续集成平台完美对接,在检查完后通过 webhook 的方式实时反馈检查结果,控制 pipeline 状态 (划重点:为 devops 量身定制的特性)。
4.不再支持原来需要输入系统用户名密码,数据库用户密码的认证方式,统一修改成 token 方式,参数配置更简便也更安全。
5.横向扩展的能力以及更快的代码检查速度。
从总体发展趋势来说,除了代码检查能力以外,对 devops 的支持能力正成为 SonarQube 升级的一个重大方向。个人认为不足之处是对 multi-maven 项目的支持还有待加强,踩坑不少。

SonarQube 简介

SonarQube 是一个用于代码质量管理的开源平台,用于管理 Java 源代码的质量。通过插件机制,SonarQube 可以集成不同的测试工具,代码分析工具,以及持续集成工具,比如 pmd-cpd、checkstyle、findbugs、Jenkins。通过不同的插件对这些结果进行再加工处理,通过量化的方式度量代码质量的变化,从而可以方便地对不同规模和种类的工程进行代码质量管理。同时 SonarQube 还对大量的持续集成工具提供了接口支持,可以很方便地在持续集成中使用 SonarQube。此外,SonarQube 的插件还可以对 Java 以外的其他编程语言提供支持,对国际化以及报告文档化也有良好的支持。

SonarQube 安装

SonarQube 安装和使用这块,网上介绍的文档已经非常多了,这里不再作为重点进行赘述.
新使用的同学可以参考官方安装指导手册:https://docs.sonarqube.org/display/SONAR/Installing+the+Server
对于像我这样的 SonarQube 老用户来说,升级的过程倒是有必要特别提下,SonarQube 最近有两个里程碑版本: 4.5.7 LTS 和 5.6 LTS,这两个版本均做了大的重构,如果小于该版本号的在升级时注意必须先升级到该 LTS 版本,再往上继续升级。
Example 1 : 4.2 -> 6.5, migration path is 4.2 -> 4.5.7 LTS -> 5.6 LTS -> 6.5
Example 2 : 5.1 -> 5.6, migration path is 5.1 -> 5.6

与 LDAP 集成

如果公司有 LDAP 统一用户认证授权系统,在 SonarQube 上进行相关集成是非常有必要的。
1.安装 ladp 插件,入口:Administration > System > Update Center,找到 ladp plugin 安装即可。
2.配置 ladp,vi conf/sonar.properties,增加 ldap 相关的配置参数

3.重启 sonar 生效

命令行方式运行

1.分析 Maven 项目
maven 全局设置,修改 setting.xml 配置

<settings>
    <pluginGroups>
        <pluginGroup>org.sonarsource.scanner.maven</pluginGroup>
    </pluginGroups>
    <profiles>
        <profile>
            <id>sonar</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <!-- Optional URL to server. Default value is http://localhost:9000 -->
                <sonar.host.url>
                  http://myserver:9000
                </sonar.host.url>
            </properties>
        </profile>
     </profiles>
</settings>

执行 maven 指令:mvn clean verify sonar:sonar -Dsonar.login=[my analysis token] -Dsonar.java.binaries=[basedir]
sonar.java.binaries 等属性也可配置在项目的 pom.xml 或构建机器的 setting.xml 里。
2.分析 Gradle 项目
配置 gradle 全局属性~/.gradle/gradle.properties.

systemProp.sonar.host.url=http://localhost:9000

gradle clean compileJava sonarqube -x test -Dsonar.projectKey=[my analysis token] -Dsonar.java.binaries=[basedir]
sonar.java.binaries 等属性也可配置在项目的 build.gradle 或构建机器的 gradle.properties 里。
详见官方文档:
maven:https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner+for+Maven
gradle:https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner+for+Gradle

与 Eclipse 集成

1.安装 sonar 的 eclipse 插件
点击 Help -> Install New Software,将弹出 Install 对话框。 复制地址http://dist.sonar-ide.codehaus.org/eclipse/ 到 Work with 栏并回车,将显示可用的插件和组件列表,如下图

2.配置 eclispe 的本地/远程 Sonar 服务器
点击 Window->Preferences->SonarQube->Servers
预置的访问本地 Sonar 服务器的地址为http://localhost:9000/ ,你可以修改、删除或者新增一个地址

3.链接你的项目
Sonar 服务器配置完成后,下一步是将你的 Eclipse 项目链接到 Sonar 服务器,并利用 Sonar 服务器进行分析。
首先,在 Project Explorer 中右键单击项目,然后点击 Configure-> Associate with SonarQueb.
若项目在 sonar 中从未运行和创建,跳过这一步。
最后执行 mvn clean verify sonar:sonar 运行即可

参考链接:http://docs.sonarqube.org/display/SONAR/SonarQube+in+Eclipse

与 Jenkins Job 集成

在 jenkins 的插件管理中选择安装 sonar jenkins plugin,该插件可以使项目每次构建都调用 sonar 进行代码度量。
进入配置页面对 sonar 插件进行配置,如下图,sonar6 只需要配置 token 即可。

创建静态代码检查的 job,以 maven 项目为例,执行 mvn clean verify sonar:sonar 即可执行 sonar 代码检查,完成后对应的 job 里会有链接直接跳转到 sonar 的检查结果。

参考链接:https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner+for+Jenkins

与 Jenkins Pipeline 集成

1.在 pipeline 中执行代码检查
SonarQube 5.2 版本以后,开始支持与 jenkins pipeline 的集成,从而正式融入到以 jenkins pipeline 为核心的持续交付工具链.
使用 pipeline 中的 “withSonarQubeEnv” 块可以选择 jenkins 中配置好的的 SonarQube server,前面的 pipeline 文章的项目样例里其实已经有相关代码,再次举例如下

withSonarQubeEnv('SonarQube') {
   //固定使用项目根目录${basedir}下的pom.xml进行代码检查
   sh "mvn -f pom.xml clean compile sonar:sonar"
 }

2.sonar 质量阈未通过时中止 pipeline
SonarQube 6.2 版本以后开始支持 webhook 功能,利用此特性可以与 jenkins 完美结合,在检查完成后通过异步回调的方式告知 jenkins 检查结果,从而让 jenkins 可以控制 pipeline 执行状态。
sonarqube 需要先配置 webhook 地址:[Your Jenkins instance]/sonarqube-webhook/,然后编写 jenkins pipeline 脚本如下。

stage('静态检查') {
           steps {
               echo "starting codeAnalyze with SonarQube......"
               //sonar:sonar.QualityGate should pass
               withSonarQubeEnv('SonarQube') {
                 //固定使用项目根目录${basedir}下的pom.xml进行代码检查
                 sh "mvn -f pom.xml clean compile sonar:sonar"
               }
               script {
               timeout(10) { 
                   //利用sonar webhook功能通知pipeline代码检测结果,未通过质量阈,pipeline将会fail
                   def qg = waitForQualityGate() 
                       if (qg.status != 'OK') {
                           error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"
                       }
                   }
               }
           }
       }

一些问题和解决方案

问题 1:maven 版本支持
From maven-sonar-plugin 3.1, Maven < 3.0 is no longer supported.If using Maven prior to 3.0, you should use maven-sonar-plugin 3.0.2.
maven-sonar-plugin3.1 以后,sonar 分析只支持 maven3 版本,maven2 不再支持。
问题 2:sonar 在创建项目时,不允许 maven 项目里的 module 被重复执行代码检查【sonar5】。
[ERROR] Module "com.greenline.eops:greenline-eops-biz" is already part of project "com.greenline.eops:greenline-eops",出现此问题的原因有几个
1.不同的项目存在重名的 module
比如说项目中的 module 是从其他项目中 copy,pom.xml 里的 artifactId 等坐标标识未作更新。
解决方案:
这类项目需要强制开发修改名称,互相不要重名。
2.multi-module 项目里多个 pom.xml 重复引用相同的 module
某个 multi-module 项目的目录结构如下

--base  pom.xml
--biz   pom.xml
--web   pom.xml
--hessian   pom.xml
--mq-client   pom.xml

在主项目下,存在 web,hessian,mq-client 等多个 module 子项目,每个 module 子项目里都会引用 biz 模块,base 目录里放的是 parent pom.xml
当 web 在 sonar 运行过代码检查后,再运行 hessian,因为 biz 里的 module 已被运行过,就会出现此类错误。
解决方案:
i.在 multi-module 项目的项目根目录 ${basedir}下,放置 parent pom.xml,并引用所有子 module。(推荐)

--biz   pom.xml
--web   pom.xml
--hessian   pom.xml
--mq-client   pom.xml
pom.xml

ii.mvn 命令运行 sonar 时增加--project ! module 参数,过滤已被运行过的 module。(不建议使用)
问题 3:还是 multi-module 项目的问题,parent module 因找不到 sonar.java.binaries 里的编译文件报错 [升级 sonar6 以后的坑]
sonar6 为了提升检测速度,在检测前会检查所有 module 的 sonar.java.binaries 里定义的编译文件是否存在(默认是/target/classes),parent 层因为没有实际项目代码也就不会产生 class 编译文件检查抛错。
sonar 社区很早就有人对此机制提出异议,搞不懂为啥现在还没有解决,sonarqube 对 multi-module 的支持确实比较糟糕。
http://sonarqube-archive.15.x6.nabble.com/Maven-Build-Fails-when-no-files-in-target-classes-directory-td5030353.html

解决方案:
目前我的解决办法是把 sonar.java.binaries 配置成项目根目录 ${basedir},sonar 会自动遍历获取根目录下所有 class 文件生成分析结果。不过这种方式对 jacoco 和 junit 的报告文件获取有一定影响(又是一个坑),大家有更好的解决方案也可以和我讨论。
为减少对项目的侵入,可在编译机里的 maven 全局配置 setting.xml 里设置,也可通过命令参数传入,配置如下:

<sonar.java.binaries>${basedir}</sonar.java.binaries>

主帖直达:https://testerhome.com/topics/9977

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 12 条回复 时间 点赞
恒温 将本帖设为了精华贴 11 Sep 19:05

感谢分享,sonar 可以关联质量阈这个涨知识了,我们之前都只是用 sonarlint 来扫描,一直苦于无法只以新增代码问题数来作为准出。

陈恒捷 回复

问题数并不是一个好的代码检查指标,我们现在是根据公司的编码规范定义了代码问题等级,阻塞和严重是必须要解决的,其他等级的只做参考。

蒋刚毅 回复

我们公司的 sonarQube 结果开发从来都不看

simple 回复

确实推动交付体系的改革需要从上而下的支持,光是一个团队一头热是难以达成的。针对 sonar 这块我的心得是把代码规则让开发的架构团队去定,毕竟他们对怎么写出好代码是最有话语权的,争取他们的支持非常重要。

陈恒捷 回复

我们是筛选了一套规则,分好优先级,新增代码有严重级以上问题不能提测。目前是提测邮件截图说明,有 pipeline 自动检查会更方便。

陈恒捷 回复

嗯,是这样的,能自动化的环节尽量都要自动化。

不错啊,就差落地了

—— 来自 TesterHome 官方 安卓客户端

关于最后一个问题
我最近也在做 sonar scanner 相关的东西,因为要集成 jacoco,所以我是把需要检测的 java 和 class 文件先分别拷贝到一个指定目录
然后在进行扫描的时候一直会有 warn,类似:Class 'org/mybatis/spring/SqlSessionTemplate' is not accessible through the ClassLoader
看了官方的文档,感觉我的配置没问题呢,楼主知道这是什么情况么

我的 sonar-project.properties 大致如下:

sonar.projectKey=XXXXXX
#Set through <version> when using Maven.
sonar.projectVersion=1.0-SNAPSHOT
#jenkins 设置SonarQube UI显示的名称别用中文
sonar.projectName=XXXXXXXXX
sonar.language=java
#代码所在路径
sonar.projectBaseDir=/export/lijuntao/source
#指定source文件路径
sonar.sources=/export/lijuntao/source

#指定class文件路径
sonar.java.binaries=/export/lijuntao/class

# 源码编码默认是系统编码
sonar.sourceEncoding=UTF-8

#Set jacoco Configuration
#Code coverage tool
sonar.java.coveragePlugin=jacoco
#Path to the JaCoCo report file containing coverage data by unit tests. The path may be absolute or relative to the project base directory
sonar.jacoco.reportPath=./jacoco.exec
sonar.jacoco.itReportPath=./jacoco.exec
#sonar.jacoco.reportMissing.force.zero
sonar.jacoco.reportMissing.force.zero=false

楼主,你好!从你分享的文章中,学习到很多,非常感谢!
公司也在使用 sonar 工具做静态代码扫描,现在使用时遇到一个问题:

1、sonar 能扫描 JDK 里的公共资源类是否关闭,但对自定义资源类是否关闭却检测不出来
2、我在研究这个问题时发现,sonar 的 squid:S3546 规则,是来解决关闭自定义资源的,但貌似只对内部类(资源类)生效,对通过 import 导包的资源类无效

请问楼主有遇到自定义资源类这个问题吗?或者请教下对这个问题有什么解决的建议吗?

simple 专栏文章:[精华帖] 社区历年精华帖分类归总 中提及了此贴 13 Dec 20:49

@ 陈恒捷 请教下增量代码扫描的问题,是通过 sonar-project.properties 来设置吗?

@ 蒋刚毅 打扰来请教个问题,pipeline 执行 sonar 扫描,获取结果的时候获取到的不是当前执行的任务结果。。

stage('静态检查') {

              withSonarQubeEnv('sonarqube-6.7.6') {
                    sh "mvn sonar:sonar"
                }
               script {
               timeout(2) { 
                   def qg = waitForQualityGate() 
                       if (qg.status != 'OK') {
                           echo "pass"                           
                       }else{
                           echo "fail"
                       }
                   }
               }

       }

Jenkins 执行的日志中

WARN: Found multiple 'report-task.txt' in the workspace. Taking the first one.
/Users/****/.jenkins/workspace/tests/test_sonar/.scannerwork/report-task.txt
/Users/****/.jenkins/workspace/tests/test_sonar/target/sonar/report-task.txt
[Pipeline] // withSonarQubeEnv
[Pipeline] script
[Pipeline] {
[Pipeline] timeout
Timeout set to expire in 2 分 0 秒
[Pipeline] {
[Pipeline] waitForQualityGate
Checking status of SonarQube task 'AWgYLcBIlPr4vtZnOdUH' on server 'sonarqube-6.7.6'
SonarQube task 'AWgYLcBIlPr4vtZnOdUH' status is 'SUCCESS'
SonarQube task 'AWgYLcBIlPr4vtZnOdUH' completed. Quality gate is 'OK'

日志中获取 task 是 AWgYLcBIlPr4vtZnOdUH 的结果,但是/Users/****/.jenkins/workspace/tests/test_sonar/target/sonar/report-task.txt 文件中却不是这个 task。 这是为什么。

流程建立起来不难,主要是执行

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up