Sonar Scanner系列之架构与Java篇

2020-12-01 09:55:46 浏览数 (1)

本文系列将介绍Sonar在实际工程项目中落地的场景,例如: 1)多语言项目的扫描,如JAVA/JS/C /C#/PLSQL 2)多分支扫描 3)覆盖率如何统计 等等。 不在讨论范围内的问题 1)自定义扫描规则? 2)扫出来的问题,怎么让开发及时修复? 本文作为开篇,将介绍 1)Sonar Scanner的工作机制, 2)Java项目中利用 Maven的Sonar Scanner 插件进行扫描的配置和步骤 3)使用Token,多Module项目扫描和忽略等一些实际问题。

与Jenkins不同的工作机制

与Jenkins类似,SonarQube也是一个C/S架构的服务。根据其官网所述,是以下的一个架构。 包括了:

  1. SonarQube服务端: 主要包括以下三部分
    • Web服务器
    • 搜索引擎-Elasticsearch to back searches from the UI
    • 后台计算服务-连接数据库
  2. 后台数据库:
    • SonarQube实例的配置信息,如安全、插件等
    • 项目、视图的质量快照数据
  3. SonarQube Plugin 安装在服务端的插件,例如语言包、SCM、认证、治理等等
  4. SonarScanner 在构建和持续集成服务器上执行并分析项目

image.png

这其中Sonar Scanner是本文所要介绍的主角。通过上述架构图中的数据流转方向,我们可以了解到Sonar和Jenkins的一个很大的不同。也就是Sonar中的客户端(Scanner)只负责数据的上报,它可以自行触发扫描,并不接受来自服务端的指令,不像Jenkins的Agent受到服务端的任务触发后才会执行。笔者在设计测试用例管理系统时,也参考了类似这样的架构,让测试用例执行的起点由客户端自行控制,只要将最终结果汇报上来即可。

解决方案一览

在公司的产品线中,既有核心的实时类C/C 程序,也有传统的C#前台 SP后台的遗留系统。目前也正在实现微服务转型,JAVA和前端JS类项目也日益多了起来。因此,我们的SonarQube质量检测服务,需要支持上述所有的类型。而根据Sonar官方提供的方案,需要用到如下的Scanner 在实践中也发现,Sonar Scanner以同一次扫描结果作为一个SonarQube Project的范围。即使指定了相同的Project Key,不同扫描器的扫描结果只会互相覆盖。因此,由于扫描器的不同,一个包含了C#、C 和PLSQL的项目,很不幸需要被三个扫描器各自扫描一次,同时生成三个SonarQube project来呈现扫描结果。 本文将分别介绍上述语言的项目中,如何利用Scanner来进行扫描。

image.png

JAVA类的项目

假设项目中使用的是Maven作为构建工具。配套的,我们通过SonarQube官方提供的SonarQube Scanner for Maven这个插件来进行代码的扫描,如果还要得到单元测试和代码覆盖率报告,那么还需要使用Maven Surefire插件以及Jacoco这样的覆盖率统计工具。 接下来逐一介绍下。

1、指定SonarQube服务器地址和口令

整个方案的基础是,让Maven中的Sonar Scanner插件能知道SonarQube服务器和登录口令。修改maven配置文件MAVEN_HOME/conf/settings.xml或者 ~/.m2/settings.xml。文件的访问顺序是:~/.m2/settings.xml优先,若无此文件,maven自动去读MAVEN_HOME/conf/settings.xml

代码语言:javascript复制
 <profiles>                       
<profile>                             
<id>sonar</id>                            
     <activation> 
            <activeByDefault>true</activeByDefault>
       </activation>                            
 <properties>                                    
      <sonar.host.url>http://IP:PORT </sonar.host.url>
<sonar.login>TOKEN </sonar.login>
</properties>                   
</profile>              
 </profiles>

另外,配置了sonar.login使用TOKEN后,不要再配置sonar.password了,不然sonar scanner会将token作为用户名去登录,导致用户名密码不匹配登陆失败。Token产生,可以参见SonarQube使用说明。需要注意的是,这个token的生成是被设计为“阅后即焚”的。在SonarQube页面上生成并关闭后,再也无法看到了,需要注意保存,否则只能再次生成了。

2、引入指定Sonar Scanner for Maven的Maven插件

方法一(推荐):修改工程的pom.xml。

代码语言:javascript复制
            <build>
                 <pluginManagement>
                     <plugins>
                        ......
                          <plugin>
                                 <groupId>org.sonarsource.scanner.maven</groupId>
                                 <artifactId>sonar-maven-plugin</artifactId>
                                 <version>3.6.0.1398</version>
                          </plugin>
                     </plugins>
                </pluginManagement>
           </build>

方法二:修改maven配置文件MAVEN_HOME/conf/settings.xml 或者 ~/.m2/settings.xml。(~/.m2/settings.xml优先,若无此文件,maven自动去读MAVEN_HOME/conf/settings.xml )

代码语言:javascript复制
               <pluginGroups>
   <pluginGroup>org.sonarsource.scanner.maven</pluginGroup>
              </pluginGroups>
3、配置单元测试执行报告的路径

配置单元测试执行报告的路径,修改最外层pom.xml 场景一:单个module工程

代码语言:javascript复制
 <properties>
  .......
  <sonar.jacoco.reportPaths>
        ${project.basedir}/target/jacoco.exec
   </sonar.jacoco.reportPaths>
 </properties>

场景二:多个modules工程

代码语言:javascript复制
  <properties>
   .......
   <sonar.jacoco.reportPaths>
        ${project.basedir}/../target/jacoco.exec
  </sonar.jacoco.reportPaths>
 </properties>
4、配置单元测试覆盖率统计插件jacoco

这个配置在网上很容易找到,为了内容的完整性还是放一下。功能就是把jacoco 挂载到maven 的各个phase/goal上去,如在单元测试和集成测试时,实现jacoco的插桩。

代码语言:javascript复制
                     <profiles>
                          <profile>
                                <id>sonar-coverage</id>
                                        <activation>
<activeByDefault>false</activeByDefault>
                                        </activation>
                                        <build>
                <pluginManagement>
                           <plugins>
                                     <plugin>
<groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
                   <version>0.8.2</version>
                         </plugin>
                               </plugins>
                               </pluginManagement>
                                <plugins>
                                       <plugin>
 <groupId>org.jacoco</groupId>
               <artifactId>jacoco-maven-plugin</artifactId>
<configuration>                                                                      <destFile>${sonar.jacoco.reportPaths}</destFile>
         <append>true</append>
                          </configuration>
                                      <executions>
                                               <execution>
                                                   <id>agent-for-ut</id>
                                                       <goals>
              <goal>prepare-agent</goal>
                                        </goals>
                                              </execution>
                                         <execution>
                                                <id>agent-for-it</id>
                                                       <goals>
                      <goal>prepare-agent-integration</goal>
                                     </goals>
                                                </execution>
                                        <execution>
                                          <id>jacoco-site</id>
                        <phase>verify</phase>
                                       <goals>
                                    <goal>report</goal>
                                            </goals>
                                    </execution>
                                            </executions>
                                     </plugin>
                                        </plugins>
                                   </build>
                                </profile>
                          </profiles>

如果需要实现集成测试/系统测试的代码覆盖率的话,则需要通过tcp等方式去dump覆盖率结果。这块不是本文的范围,就不展开了。

5、实施扫描

如果启用了分支,就需要分两次执行扫描。如果未使用的话,则一次扫描即可。 第一次扫描,先初始化执行master分支扫描 构建步骤增加 ”mvn sonar:sonar 不指定分支名字,默认是将扫描结果归属到master分支。 第二次扫描,指定分支名称 ”mvn sonar:sonar -Dsonnar.branch.name=${branchName}“** 如果挂载到了maven某个生命周期的某个阶段上,则执行 mvn clean install也可实现上述目的。

6、杂项
  1. 若只想做静态代码扫描,不执行测试用例和覆盖率,则在 mvn clean compile后执行sonar即可。

2)为了确保工程有单元测试执行结果,以便于让Sonar统计测试结果,需要忽略失败的测试结果,强制让Maven surefire插件生成测试报告 mvn clean test -Dmaven.test.failure.ignore=true 注意一定要加****-Dmaven.test.failure.ignore=true* **参数哦。

  1. 如何忽略用例, a) 忽略某个modules工程,在该module下配置 <sonar.skip>true</sonar.skip> 即可 b) 忽略如测试用例或其它某些package或.java文件,则配置<sonar.exclusions>[...]</sonar.exclusions>。

【未完待续】 1)如果一个项目中包含C /C#/PLSQL多种语言,如何实施SonarQube扫描?需要扫几次,是几个项目? 2)社区版本的SonarQube没有扫描C /PLSQL等语言的能力,怎么办? 3)如果代码库有多个分支,如何为每个分支产生扫描结果?社区版好像没有这个功能哎,怎么办? 4)为什么C 项目扫出来缺陷、安全漏洞都是0?覆盖率也是0%?

0 人点赞