Sonarqube8.9社区版(已安装插件Community Branch Plugin 1.8.1
)的新代码检查策略有3种,分别是:
- 上个版本(新代码周期会从上个版本的分析开始计算)
- 天数(使用指定天数作为新代码周期的浮动窗口)
- 引用分支(为新代码选择引用分支)
采用不同的代码分支管理策略,可以选择不同的新代码策略。
- 如果是单分支串行开发,则可以选择
上个版本
作为新代码的策略; - 如果是多特性分支开发,master作为发布分支,则可以选择
引用分支
作为新代码的策略;
上个版本
需要注意的是 项目的版本号不能随意的修改,因为如果修改了版本号,sonarqube就会认为是一个新的开始,新代码就会从你修改那一刻开始计算。
引用分支
一般是:特性分支配置为引用分支
(选择基准分支为发布分支,如:master),然后master的新代码策略则选择为:上个版本
。
问题现象
每个特性分支开始都是从master分支新建过来的。每次sonar扫描后,都会以master进行比较。
但此时问题就出现了。现象如下:
从上面的图中可以看到:
- fea/jira-1从创建分支到上线,一共提交了2次,新代码一共150行;
- fea/jira-2从创建分支到上线,一共提交了3次,新代码一共130行,期间中途合并了一次master代码。
每次提交代码后,触发分支的流水线进行扫描sonar质量,fea/jira-1每次都没有问题,新代码统计正确,第一次提交后,新代码显示50行,第二次提交后,新代码显示150行。
fea/jira-2前面两次提交后,新代码显示70行,也没问题。合并master后,新代码却显示0行,然后提交代码3后,新代码显示60行。问题就出现在这里!按道理来说新代码显示应该是130行,但却因为合并导致新代码计算错误。
下图是合并master代码后的sonar扫描结果:
问题原因
经过分析sonarqube 8.9的源码,代码模块是sonar-scanner-engine
,关键的类是:org.sonar.scanner.repository.ForkDateSupplier
和org.sonar.scm.git.GitScmProvider
。
8.9版本的新代码是通过ForkDateSupplier进行计算。
ForkDateSupplier.java
@CheckForNull
public Instant get() {
// branches will be empty in CE
if (branchConfiguration.isPullRequest() || branches.isEmpty()) {
return null;
}
Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG_WS);
String branchName = branchConfiguration.branchName() != null ? branchConfiguration.branchName() : branches.defaultBranchName();
NewCodePeriods.ShowWSResponse newCode = newCodePeriodLoader.load(project.key(), branchName);
profiler.stopInfo();
if (newCode.getType() != NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH) {
return null;
}
String referenceBranchName = newCode.getValue();
if (branchName.equals(referenceBranchName)) {
LOG.warn("New Code reference branch is set to the branch being analyzed. Skipping the computation of New Code");
return null;
}
LOG.info("Computing New Code since fork with '{}'", referenceBranchName);
if (scmConfiguration.isDisabled() || scmConfiguration.provider() == null) {
LOG.warn("SCM provider is disabled. No New Code will be computed.");
analysisWarnings.addUnique("The scanner failed to compute New Code because no SCM provider was found. Please check your scanner logs.");
return null;
}
Instant forkdate = scmConfiguration.provider().forkDate(referenceBranchName, project.getBaseDir());
if (forkdate != null) {
LOG.debug("Fork detected at '{}'", referenceBranchName, forkdate);
} else {
analysisWarnings.addUnique("The scanner failed to compute New Code. Please check your scanner logs.");
LOG.warn("Failed to detect fork date. No New Code will be computed.", referenceBranchName);
}
return forkdate;
}
上面的Instant forkdate = scmConfiguration.provider().forkDate(referenceBranchName, project.getBaseDir())
就是在计算当前分支(fea/jira-2)和目标分支(master)的最近一次merge的时间点。
所以就能理解为什么fea/jira-2的新项目,为什么在merge master代码后,新代码的数据就从新开始算了。
解决办法
在不修改源码的情况下,怎么解决呢?提供两种方法:一是通过 pull request的方式来解决(但此种方式,sonar扫描后只能看到新代码相关的指标,不能看到全量代码的指标数据),二是通过升级sonarqube版本来解决(测试了9.3的版本,是没问题的,)
方法一
正常情况下:执行mvn sonar:sonar会默认当成分支处理模式,为了能让其变为 pull request
的模式,可以在执行命令时增加3个属性,如下:
- mvn sonar:sonar -Dsonar.pullrequest.branch=fea/jira-2 -Dsonar.pullrequest.base=master -Dsonar.pullrequest.key=fea/jira-2
如果是在gitlab-ci的流水线环境中,可以用如下的配置配置:
Commit-CodeAnalysis:
stage: Commit-CodeAnalysis
variables:
branch_name: $CI_COMMIT_REF_NAME
script:
- unset CI_COMMIT_REF_NAME CI_COMMIT_REF_SLUG
- mvn sonar:sonar -Dsonar.pullrequest.branch=$branch_name -Dsonar.pullrequest.base=$CI_DEFAULT_BRANCH -Dsonar.pullrequest.key=$branch_name
$CI_COMMIT_REF_NAME 表示的当前分支
$CI_DEFAULT_BRANCH 表示的主分支(这个项目配置的是master)
注意在实现sonar:sonar命令前,移除了两个属性unset CI_COMMIT_REF_NAME CI_COMMIT_REF_SLUG,目的主要是让sonar判断为这是pull request
模式,而不是分支模式。
扫描后的结果截图如下:
下图中:
- 第一个红框 对应的是:sonar.pullrequest.key
- 第二个红框 对应的是:sonar.pullrequest.branch
- 第三个红框 对应的是:sonar.pullrequest.base
- 第四个红框 这表示的是 当前分支fea/jira-2和master比较 新增了130行代码。
但从上图中 看不到 全部代码的选项卡。
上面的解决方式只是用 pull rquest的方式(模拟发起合并代码请求)的方式来解决,效果达到了,但解决方法并不优雅。
方法二
升级sonarqube到最新版,目前最新版是9.3 (build 51899)。升级方法:见官方文档(Upgrade Guide)。(在写这篇文章时,官方已经推送了9.4版本的更新)
- 遇到的问题
如果我们的项目是Java项目,会遇到JDK版本不兼容的问题。
sonarqube从9.X开始,不再支持jdk8,需要用jdk11+才能运行。
如果我们项目项目用的是jdk1.8,同时还使用了1.8里面的一些后续版本不在兼容的包类,如:rt.jar里面的sun.misc.BASE64Decoder
、javax.xml.bind.Marshaller
等。用jdk11来运行就会报错。
mvn clean test sonar:sonar
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project iccboy-sonar-test: Compilation failure
[ERROR] /opt/code/iccboy-sonar-test/iccboy-sonar/iccboy-sonar-common/src/main/java/com/iccboy/sonar/test/common/utils/FileUtil.java:[8,16] 找不到符号
[ERROR] 符号: 类 BASE64Decoder
[ERROR] 位置: 程序包 sun.misc
如果我们用jdk8来执行,则优化报sonar的class编译文件解析不对的问题(Java.lang.UnsupportedClassVersionError)。
mvn clean test sonar:sonar
[ERROR] Failed to execute goal org.sonarsource.scanner.maven:sonar-maven-plugin:3.9.0.2155:sonar (default-cli) on project iccboy-sonar-test: Execution default-cli of goal org.sonarsou
rce.scanner.maven:sonar-maven-plugin:3.9.0.2155:sonar failed: An API incompatibility was encountered while executing org.sonarsource.scanner.maven:sonar-maven-plugin:3.9.0.2155:sonar:
java.lang.UnsupportedClassVersionError: org/sonar/batch/bootstrapper/EnvironmentInformation has been compiled by a more recent version of the Java Runtime (class file version 55.0), th
is version of the Java Runtime only recognizes class file versions up to 52.0
[ERROR] -----------------------------------------------------
[ERROR] realm = plugin>org.sonarsource.scanner.maven:sonar-maven-plugin:3.9.0.2155
[ERROR] strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
[ERROR] urls[0] = file:/opt/repository/org/sonarsource/scanner/maven/sonar-maven-plugin/3.9.0.2155/sonar-maven-plugin-3.9.0.2155.jar
[ERROR] urls[1] = file:/opt/repository/org/sonatype/plexus/plexus-sec-dispatcher/1.4/plexus-sec-dispatcher-1.4.jar
[ERROR] urls[2] = file:/opt/repository/org/sonatype/plexus/plexus-cipher/1.4/plexus-cipher-1.4.jar
[ERROR] urls[3] = file:/opt/repository/org/codehaus/plexus/plexus-utils/3.2.1/plexus-utils-3.2.1.jar
[ERROR] urls[4] = file:/opt/repository/org/sonarsource/scanner/api/sonar-scanner-api/2.16.1.361/sonar-scanner-api-2.16.1.361.jar
[ERROR] urls[5] = file:/opt/repository/commons-lang/commons-lang/2.6/commons-lang-2.6.jar
[ERROR] Number of foreign imports: 1
[ERROR] import: Entry[import from realm ClassRealm[maven.api, parent: null]]
为了解决上面的问题,则将命令分步骤执行。
第一步先用jdk8执行项目的编译和测试。
第二部在用jdk11执行sonar命令。
如下:
export JAVA_HOME=/usr/local/jdk8/jdk1.8.0_211
mvn clean test
export JAVA_HOME=/usr/local/jdk11/jdk-11.0.14
mvn sonar:sonar
如果是windows环境,则将
export
改为set
从sonarqube的github提交记录来看,在2022-1-11号开始进行了问题的修复。目前在9.3+版本已经修复了这个问题。
SONAR-14929 New Code using a 'reference branch' doesn't detect changed code with git merge workflow
3ec97d1a Duarte Meneses <duarte.meneses@sonarsource.com> on 2022/1/11 at 0:09
committed by sonartech <sonartech@sonarsource.com> on 2022/1/22 at 4:03
这次提交中做了很多的更改,上文中的ForkDateSupplier
也改为了ReferenceBranchSupplier
。
最后也希望官方能在sonarqube8.9(LST)的版本中修复该问题。
其他
在使用了一段时间9.3版本后,发现在另外的方面存在一些问题。主要表现是,项目首页显示新增代码有问题,但点击去看,又显示没有问题。
解决办法: 升级到最新版9.4(54424) 解决了这个问题。(但9.4也有另外的问题(影响不大),把问题标记为关闭不修复后,首页的新问题未同步减少,需要重新扫描代码才生效,之前在8.9版本没有这个问题),在9.5版本已经解决了9.4版本的问题 nice(2022年6月26日)