本文主要介绍SonarScanner在对不同语言项目进行代码扫描时需要注意的一些事项,以及Sonar与Jenkins集成实现项目代码自动化扫描,重点是介绍一种在Jenkins上配置代码扫描的万能方法,该方法既能扫描不同类型的项目(Maven、Gradle、Python、Go、PHP、前端项目)又能够实现多项目并行代码扫描,这样能给开发代码扫描自动化功能带来极大便利,再也不需要考虑不同业务线到底是什么类型的项目了。
这是官网上一张SonarQube使用流程图:

典型的开发过程中:
1、开发人员可以在IDE中集成SonarLint,开发过程中可以提示编码存在的问题;开发完成后将代码合并到git等代码仓库
2、持续集成CI工具(如Jenkins等)可以拉取代码,然后通过集成SonarQube扫描器进行代码扫描,获取代码扫描结果;
3、扫描器将扫描结果发布到SonarQube服务器上,该服务器可以通过SonarQube接口、email、IDE内通知(通过SonarLint)向开发人员提供反馈。
一、Sonar代码扫描平台的组成
Sonar代码扫描由两部分组成:一个是SonarQube数据展示平台(相当于server端)、一个是SonarScanner扫描器(相当于client端)。每个版本的SonarQube(目前最新版本8.7)都有对应版本的SonarScanner扫描器,大家可以官网下载。
1、官网地址:https://docs.sonarqube.org/8.7/
2、SonarQube平台主要用来
—展示所有项目代码扫描结果的质量数据
—配置质量规则、管理项目、配置通知等
3、SonarScanner扫描器主要用来
—专门用例扫描和分析代码的,目前能扫描20多种语言
—首先得配置SonarQube平台的url地址
—代码扫描和分析完成后,会将扫描结果发送给SonarQube,SonarQube做数据汇总存储在SonarQube的数据库(目前新版本支持postgreSql、Oracle、SqlServer,老版本7.9以下支持mysql)

二、SonarScanner代码扫描器
SonarScanner进行代码扫描时,你的代码必须在本地,扫描器不会访问远程代码仓库进行扫描,你必须使用git、svn或者其他手段将远程代码下载到本地,再通过本地的SonarScanner进行扫描。
不同语言开发的项目的需要配置不同的扫描器,目前有Maven、Gradle、.NET和其他 (比如 Go, Python, PHP, ...)

下面分别以python项目、maven项目、gradle项目为例,分别介绍不同扫描器的注意事项。
1、扫描python项目(go类似)
1)首先下载下图所示的SonarScanner扫描器,实际就是一个压缩包,解压即可(如下图所示);

2)再修改conf文件夹下sonar-scanner.properties配置文件


3)下载源码,在源码目录新建sonar-project.properties文件,里面添加如下信息

sonar.projectKey=LearDjango #唯一标识,不同项目key必须不一样,SonarQube展示平台通过key区分不同项目sonar.projectName=LearDjango #一般与可以相同sonar.sources=apps/ #指定扫描源码的目录,使用相对路径sonar.language=python #指定扫描的语言soar.sourcesEncoding=UTF-8 #编码格式#更多参数可以参照官网:https://docs.sonarqube.org/latest/analysis/analysis-parameters
4)在项目源码目录运行命令:sonar-scanner即可。等待扫描完成,就可以在SonarQube展示平台看到扫描结果。
注意:
1)如果无法在项目的根目录中创建sonar-project.properties文件,则有以下几种选择替代sonar-project.properties:
—可以直接通过命令行指定属性
sonar-scanner -Dsonar.projectKey=myproject -Dsonar.sources=src1
2)属性project.settings可用于指定项目配置文件的路径(此选项与sonar.projectBaseDir属性不兼容)
sonar-scanner -Dproject.settings=../myproject.properties
2、扫描Maven项目
1)下载apache-maven-3.6.3,直接解压即可
2)在conf目录下的settings.xml下进行配置:包括sonar插件配置、sonarQube平台的url地址等
<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://xxx.xxx.xx.xxx:9000</sonar.host.url></properties></profile></profiles></settings>
3)从git上下载源码到服务器
4)在源码pom.xml文件所在目录新建sonar-project.properties文件,文件里需写好扫描的配置内容,需要指定sonar.java.binaries路径

5)必须在服务器上源码目录执行命令:
mvn clean verify sonar:sonar
6)、也可以不用第四步新建文件,可以在第5步指令中配置属性:
mvn clean verify sonar:sonar-Dsonar.host.url=http://xxx.xxx.xx.xxx:9000/-Dsonar.login=admin-Dsonar.password=admin-Dsonar.projectKey=my_project1-Dsonar.projectName=my_project1-Dsonar.java.binaries=target/classes
3、扫描Gradle项目
1)下载gradle-6.8.3-all
2)从git上下载源码到服务器
3)修改源码目录下build.gradle文件,添加sonarqube插件配置,指定sonarqube服务器地址等
plugins {id "org.sonarqube" version "3.1.1"}sonarqube {properties {property "sonar.host.url", "http://localhost:9000"property "sonar.login", "admin"property "sonar.password", "admin"}
4)、Gradle 默认配置了SonarQube properties,不需要我们配置。如果我们非要自定义配置,可以在执行命令时配置,参考第6步;

5)、源码目录执行命令:
gradel sonarqube
6)、也可以在build.gradle不配置sonarqube的属性url、用户名和密码,直接在执行命令时添加
gradel sonarqube -Dsonar.host.url=http://xx.xxx.xxx.xxx:9000/-Dsonar.login=admin-Dsonar.password=admin-Dsonar.projectKey=my_project1......
三、Sonar与Jenkins结合实现自动化
这里介绍一种创建代码扫描的万能方法,该方法
—既可以扫描Maven类型项目,又可以扫描Gradle类型项目,又可以扫描Python、Go等类型项目;—既可以扫描单模块的项目,又可以扫描多模块的项目;—既可以单任务扫描,又可以多任务并行扫描;
1)Jenkins上配置安装SonarQube Server、Maven、Gradle、SonarQube Scanner。
在Jenkins“系统管理-》配置-》SonarQube servers”配置SonarQube平台地址;
在Jenkins“系统管理-》全局工具配置-》Maven/Gradle/SonarQube Scanner”安装工具。注意:这里安装多个SonarQube Scanner,为多项目并行代码扫描做准备。




2)选择“新建任务-》构建一个自由风格的软件项目”。当然你也可以通过构建一个“流水线”pipeline项目来实现,这样你需要熟悉pipeline脚本语法规则,可以从官网直接学习一些pipeline的案例。这里介绍构建一种自由风格Job的方式。

3)、在“源码管理”这里配置好git仓库url、用户名和密码、分支。注意这里的既可以填写分支名称也可以填写commitID。

4)、因为这里我们需要能够扫描不同类型的项目,所以我们需要条件构建。这里的条件就是我们需要判断不同的项目类型,然后执行不同的扫描配置。由于Maven和Gradle构建的项目需要进行一些配置才能使用扫描器进行扫描,而Python、Go、PHP等不需要配置就可以直接进行代码扫描。那么这里我们就可以分为三个条件判断:Maven项目、Gradle项目、其他项目。
在构建步骤中添加:Condition steps(multiple)。Condition steps(single)和Condition steps(multiple)的区别是当条件满足时一个只能执行一步动作,而一个可以执行多步动作。

5)、条件一:判断Maven项目。判断条件很简单:只需判断工作目录下是否含有pom.xml文件即可。条件判断成功后,只需将项目先编译就行。

注意:扫描器会同时扫描单元测试的代码,如果有单元测试没有通过,会终止整个扫描任务。通过配置如下命令就可以继续执行扫描任务,忽略失败的测试。
-Dmaven.test.failure.ignore=true
6)、条件二:判断Gradle项目。判断条件同样很简单:只需判断工作目录下是否含有build.gradle文件即可。条件判断成功后,先修改build.gradle配置文件,然后直接构建build即可。
id "org.sonarqube" version "2.7"

7)代码扫描。如果这两个条件都不满足,说明是其他项目,直接进行代码扫描即可。
在构建步骤中添加:Execute SonarQube Scanner。配置SonarQube平台名称,JDK版本、扫描器名称、扫描器属性。


注意:
1、sonar.source和sonar.java.binaries都指定为当前源码目录($WORKSPACE是Jenkins内置环境变量),因为不同项目的代码和编译构建后的class目录都不一致,有的源码在src/main下,有的构建后目录在target/classes,但是不同项目均不一致,故都指定为当前源码目录,这样扫描器就会从当前源码目录自动查找。
2、并行扫描。如果是多个Job并行扫描,这里可以配置不同的扫描器名称,让多个扫描器同时扫描,减少扫描时间。
四、参数化构建
如果想通过一个Job就可以拉取不同项目的代码,可以把git仓库的地址和分支实现参数化构建,可以添加不同类型的参数,然后使用${参数名}动态获取参数。


五、Pipeline示例
这里列举一个简单的Pipeline的示例,具体语法可以参考Jenkins官网,这里就不作详细解释了。
pipeline{agent anystages {stage('拉取代码') {steps{script {def scmVars = checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'XXXX-XXX-XXX-XX-XXXX', url: 'git@XXXXXXXXX.git']]])commit="${scmVars.GIT_COMMIT}"}}}stage('mvn构建') {steps{sh label: '', script: 'mvn clean install -Dmaven.test.failure.ignore=true'}}stage('sonarqube代码扫描') {steps {script {def sonarqube = "true"//这个是安装到sonarscaner插件到名字scannerHome = tool 'SonarQubeScanner1'echo "$sonarqube"if (sonarqube == "true") {//提交到哪个环境分析withSonarQubeEnv('SonarQube189') {sh "${scannerHome}/bin/sonar-scanner"}} else {echo "未进行代码扫描"}}}}}}




