hugo-teek is loading...

代码质量平台实践-SonarQube

最后更新于:

代码质量平台实践-SonarQube

image-20220524071501389

目录

[toc]

推荐文章

https://www.yuque.com/xyy-onlyone/aevhhf?# 《玩转Typora》

image-20230624094222589

本节实战

实战名称
💘 实践:Scanner进行项目代码扫描(测试成功)-2023.6.24
💘 实践:Jenkins集成SonarQube(命令行方式)(测试成功)-2023.6.24
💘 实践:Jenkins集成SonarQube(jenkins插件方式)(测试成功)-2023.6.24
💘 实践:新建SonarQube质量配置(测试成功)-2023.6.25
💘 实践:新建SonarQube质量阈(测试成功)-2023.6.25
💘 实践:SonarQube多分支代码扫描(测试成功)-2023.6.25
💘 实践:SonarQube控制代码扫描步骤运行(测试成功)-2023.6.25

1、SonarQube 简介

SonarQube®是一种自动代码审查工具,可检测代码中的错误,漏洞和代码味道。它可以与您现有的工作流程集成,以实现跨项目分支拉取请求的持续代码检查。

开发人员在IDE开发代码,可以安装SonarLint插件(本地代码扫描)进行提交前代码扫描。

当开发人员提交代码到版本控制系统中,自动触发jenkins进行代码扫描。

sonarQube本身具有消息通知webhook,当然也可以借助Jenkins的email插件来发送消息通知。

组件与服务组成:

  • SonarQube Server启动包含3个主要进程:

    • Web服务器,供开发人员,管理人员浏览高质量的快照并配置SonarQube实例
    • 基于Elasticsearch的Search Server从UI进行搜索服务。
    • Compute Engine服务器,负责处理代码分析报告并将其保存在SonarQube数据库中。
  • SonarQube数据库要存储:SonarQube实例的配置(安全,插件设置等)项目,视图质量快照。

  • 服务器上安装了多个SonarQube插件,可能包括语言,SCM,集成,身份验证和管理插件。

  • 在持续集成服务器上运行一个或多个SonarScanner,以分析项目。

image-20220524083149754

image-20220524083202856

image-20230623164408474

2、SonarQube平台安装配置

==见单独md。==

3、SonarScanner使用方法

代码扫描过程: 本地(构建节点)安装配置SonarScanner环境,然后通过设置sonar的一系列参数进行扫描分析。

  • 配置文件方式读取扫描参数 (推荐)
  • 命令行方式读取扫描参数

==方法1:配置文件方式读取扫描参数==

一个基本的sonar-project.properties配置文件的参数:

 1# 定义唯一的关键字
 2sonar.projectKey=devops6-maven-service
 3
 4# 定义项目名称
 5sonar.projectName=devops6-maven-service
 6
 7# 定义项目的版本信息
 8sonar.projectVersion=1.0
 9 
10# 指定扫描代码的目录位置(多个逗号分隔)
11sonar.sources=.
12 
13# 执行项目编码
14sonar.sourceEncoding=UTF-8
15
16# 指定sonar Server
17sonar.host.url=http://172.29.9.101:9000
18
19# 认证信息
20sonar.login=admin
21sonar.password=Admin@123

这些配置项都是统一的,目前sonar支持将扫描参数以文件的方式存放或者以命令行传参的方式读取。

文件方式:可以将扫描参数放到项目的根目录或者sonar-scanner的配置文件目录等自定义的目录中;

命令行传参则可以直接将变量传递给sonarsacnner cli -Dsonar.projectKey=xxx 。

==方法2:命令行方式读取扫描参数== (命令行方式会覆盖掉配置文件方式的)

1# 指定配置文件
2sonar-scanner -Dproject.settings=myproject.properties
3
4# 命令行传参
5sonar-scanner -Dsonar.projectKey=myproject -Dsonar.sources=src1

1.Java项目扫描

sonarqube服务器端需要安装Java语言规则插件

image-20230624112316659

 1sonar-scanner -Dsonar.host.url=http://192.168.1.200:9000 \
 2-Dsonar.projectKey=devops-maven-service \
 3-Dsonar.projectName=devops-maven-service \
 4-Dsonar.projectVersion=1.1 \
 5-Dsonar.login=admin \
 6-Dsonar.password=admin123 \
 7-Dsonar.ws.timeout=30 \
 8-Dsonar.projectDescription="my first project!" \
 9-Dsonar.links.homepage=http://192.168.1.200/devops/devops-maven-service \
10-Dsonar.links.ci=http://192.168.1.200:8080/job/demo-pipeline-service/ \
11-Dsonar.sources=src \
12-Dsonar.sourceEncoding=UTF-8 \
13-Dsonar.java.binaries=target/classes \
14-Dsonar.java.test.binaries=target/test-classes \
15-Dsonar.java.surefire.report=target/surefire-reports
 1`sonar.projectKey` 指定项目的关键字
 2`sonar.host.url`指定服务器地址(可以直接在配置文件中写死),
 3`projectName`指定项目的名称, 
 4`projectVersion`指定项目的版本(可以用构建时间和构建ID定义),
 5`login`指定登录用户名,
 6`password`指定登录用户密码,
 7`projectDescription`指定项目的描述信息, 
 8`links.homepage`指定项目的主页(超链接),
 9`sources`指定扫描的目录, 
10`sourceEncoding`指定扫描时的编码, 
11`java.binaries`指定编译后的类文件目录(必填),
12 `java.test.binaries`指定编译后的测试类目录,
13`java.surefire.report`指定测试报告目录。
💘 实践:Scanner进行项目代码扫描(测试成功)-2023.6.24

image-20230624112921445

  • 实验环境
1sonarqube:9.9.0-community (docker方式部署)
2SonarScanner 4.8.0.2856 (部署在宿主机上)
  • 实验软件(无)

  • 手动扫描代码

devops6-maven-service项目里创建sonar-project.properties文件:

 1# 定义唯一的关键字
 2sonar.projectKey=devops6-maven-service
 3
 4# 定义项目名称
 5sonar.projectName=devops6-maven-service
 6
 7# 定义项目的版本信息
 8sonar.projectVersion=1.0
 9 
10# 指定扫描代码的目录位置(多个逗号分隔)
11sonar.sources=.
12 
13# 执行项目编码
14sonar.sourceEncoding=UTF-8
15
16# 指定sonar Server
17sonar.host.url=http://172.29.9.101:9000
18
19# 认证信息
20sonar.login=admin
21sonar.password=Admin@123
22
23# java classes
24sonar.java.binaries=target/classes 
25sonar.java.test.binaries=target/test-classes 
26sonar.java.surefire.report=target/surefire-reports

image-20230624115524654

提交。

  • 在本地devops6-maven-service本地项目里,拉取代码,然后扫描
 1#拉取代码
 2[root@Devops6 devops6-maven-service]#pwd
 3/data/devops6/devops6-maven-service
 4[root@Devops6 devops6-maven-service]#ls
 5mvnw  mvnw.cmd  pom.xml  src  target
 6[root@Devops6 devops6-maven-service]#git pull
 7[root@Devops6 devops6-maven-service]#ls
 8mvnw  mvnw.cmd  pom.xml  sonar-project.properties  src  target
 9
10#删除之前的打包好的缓存数据
11[root@Devops6 devops6-maven-service]#rm -rf target/
12
13#打包
14[root@Devops6 devops6-maven-service]#mvn clean package
15
16#扫描
17[root@Devops6 devops6-maven-service]#sonar-scanner

image-20230624120241295

image-20230624120314686

  • 查看扫描报告

image-20230624120342846

测试结束。😘

💘 实践:Scanner进行项目代码扫描(测试成功)-2022.5.24

自己实际测试过程:

  • 克隆devops4-maven-service项目代码到sonarscanner机器:
 1[root@devops ~]#mkdir sonarProject
 2[root@devops ~]#cd sonarProject/
 3[root@devops sonarProject]#git clone http://172.29.9.101/devops4/devops4-maven-service.git
 4Cloning into 'devops4-maven-service'...
 5Username for 'http://172.29.9.101': root
 6Password for 'http://root@172.29.9.101': 
 7remote: Enumerating objects: 52, done.
 8remote: Counting objects: 100% (18/18), done.
 9remote: Compressing objects: 100% (18/18), done.
10remote: Total 52 (delta 9), reused 0 (delta 0), pack-reused 34
11Receiving objects: 100% (52/52), 56.33 KiB | 28.16 MiB/s, done.
12Resolving deltas: 100% (11/11), done.
13[root@devops sonarProject]#ls
14devops4-maven-service
15[root@devops sonarProject]#cd devops4-maven-service/
16[root@devops devops4-maven-service]#ls
17build.sh  mvnw  mvnw.cmd  pom.xml  README.md  src
18[root@devops devops4-maven-service]#
  • 在该项目目录下创建sonar-project.properties文件:
 1[root@devops devops4-maven-service]#pwd
 2/root/sonarProject/devops4-maven-service
 3[root@devops devops4-maven-service]#ls
 4build.sh  mvnw  mvnw.cmd  pom.xml  README.md  src
 5[root@devops devops4-maven-service]#vim sonar-project.properties
 6# 定义项目关键字
 7sonar.projectKey=devop4-maven-service
 8
 9# 定义项目名称
10sonar.projectName=devops4-maven-service
11
12# 定义项目的版本信息
13sonar.projectVersion=1.0
14 
15 # 指定扫描代码的目录位置(多个逗号分隔)
16sonar.sources=src
17  
18  # 执行项目编码
19sonar.sourceEncoding=UTF-8
20
21  # 指定sonar Server
22sonar.host.url=http://172.29.9.101:9000
23
24  # 认证信息
25sonar.login=admin
26sonar.password=admin123 
  • 进行扫描:
1[root@devops devops4-maven-service]#sonar-scanner

此时会发现一个常见的报错:

注意:java类型项目比较特殊,扫描时要先对其进行下编译,因为sonarqube在扫描时需要用到它编译后的类!

  • 此时再编辑下sonar-project.properties内容,并编译下后,再执行下扫描:
 1[root@devops devops4-maven-service]#vim sonar-project.properties
 2# 定义项目关键字
 3sonar.projectKey=devop4-maven-service
 4
 5# 定义项目名称
 6sonar.projectName=devops4-maven-service
 7
 8# 定义项目的版本信息
 9sonar.projectVersion=1.0
10 
11 # 指定扫描代码的目录位置(多个逗号分隔)
12sonar.sources=src
13  
14  # 执行项目编码
15sonar.sourceEncoding=UTF-8
16
17  # 指定sonar Server
18sonar.host.url=http://172.29.9.101:9000
19
20  # 认证信息
21sonar.login=admin
22sonar.password=admin123
23
24sonar.java.binaries=target/classes 
25
26[root@devops devops4-maven-service]#mvn clean package
27[root@devops devops4-maven-service]#ls
28build.sh  mvnw  mvnw.cmd  pom.xml  README.md  sonar-project.properties  src  target
29[root@devops devops4-maven-service]#ls target/
30classes                  demo-0.0.1-SNAPSHOT.jar.original  generated-test-sources  maven-status
31demo-0.0.1-SNAPSHOT.jar  generated-sources                 maven-archiver          test-classes
32[root@devops devops4-maven-service]#ls target/classes/
33application.properties  com  static

再次扫描下,查看结果:

1[root@devops devops4-maven-service]#ls .scannerwork/
2css-bundle  report-task.txt
3[root@devops devops4-maven-service]#cat  .scannerwork/report-task.txt 
4projectKey=devop4-maven-service
5serverUrl=http://172.29.9.101:9000
6serverVersion=8.9.8.54436
7dashboardUrl=http://172.29.9.101:9000/dashboard?id=devop4-maven-service
8ceTaskId=AYD2jY87VkQu9X-kQ24q
9ceTaskUrl=http://172.29.9.101:9000/api/ce/task?id=AYD2jY87VkQu9X-kQ24q
  • 到sonarqube server上验证: http://172.29.9.101:9000/dashboard?id=devop4-maven-service 这里可以设置为误判 注意:soarqube会把源代码下载下来的,因此一定要保护好sonarqube的账户信息。 可以看到这里是1.0版本:

  • 此时我们修改下sonar-project.properties的sonar.projectVersion=1.0

1[root@devops devops4-maven-service]#vim sonar-project.properties
2sonar.projectVersion=2.0

再次扫描下,再来看下效果: sonar-scanner 此时就可以看到新代码这里显示的bug数了:

⚠️ 注意:这里的sonar-project.properties文件名是不能变的!

测试结束。😘

2.Web前端项目扫描

image-20220526160925155

 1sonar-scanner \
 2  -Dsonar.projectKey=demo-devops-ui \
 3  -Dsonar.projectName=demo-devops-ui \
 4  -Dsonar.sources=src \
 5  -Dsonar.host.url=http://192.168.1.200:9000 \
 6  -Dsonar.login=0809881d71f2b06b64786ae3f81a9acf22078e8b \
 7  -Dsonar.projectVersion=2.0 \
 8  -Dsonar.ws.timeout=30 \
 9  -Dsonar.projectDescription="my first project!" \
10  -Dsonar.links.homepage=http://192.168.1.200/devops/devops-maven-service \
11  -Dsonar.links.ci=http://192.168.1.200:8080/job/demo-pipeline-service/ \
12  -Dsonar.sourceEncoding=UTF-8 

3.Golang项目扫描

image-20220526160958659

 1sonar-scanner -Dsonar.projectKey=devops-golang-service \
 2-Dsonar.projectName=devops-golang-service \
 3-Dsonar.sources=src \
 4-Dsonar.login=admin \
 5-Dsonar.password=admin \
 6-Dsonar.host.url=http://192.168.1.200:9000
 7
 8
 9## 有测试用例的情况
10sonar.exclusions=**/*_test.go
11sonar.tests=.
12sonar.test.inclusions=**/*_test.go

4、CI流水线集成

1.JenkinsPipeline集成

本次集成重点演示两种方式: 1. 使用命令行方式 2. 使用Jenkins扩展插件的方式。

(1)使用命令行方式

💘 实践:Jenkins集成SonarQube(命令行方式)(测试成功)-2023.6.24
  • 实验环境
1jenkins/jenkins:2.346.3-2-lts-jdk11
2gitlab/gitlab-ce:15.0.3-ce.0
3sonarqube:9.9.0-community
4SonarScanner 4.8.0.2856
  • 实验软件

链接:https://pan.baidu.com/s/1TWnlWTaHP_XP3O1aErXCdA?pwd=0820 提取码:0820 2023.6.24-Jenkins集成SonarQube-code

image-20230624223946529

  • 先在jenkins里配置下devops6-maven-service共享库设置

image-20230624210950231

image-20230624211006713

  • 先手动构建下,测试下流水线

image-20230624211038878

image-20230624211108545

构建成功。

  • 我们来看下gitlab devops6-maven-servicesonar-project.properties文件里是不是存在一些问题,需要修改的呢?

image-20230624211314245

这里的SnoarQube账号密码怎么能用明文来显示呢。显示是存在问题的。

Valut专门用来管理秘钥的,那是目前来说最安全的一种方式了。

  • 在新建一个Jenkins凭据

image-20230624211449078

image-20230624212305103

同时利用Jenkinbs的片段生成器来生成代码:

image-20230624214350156

1withCredentials([usernamePassword(credentialsId: '003d1667-653e-40f3-8103-b74614643aba', passwordVariable: 'SONAR_PASSWD', usernameVariable: 'SONAR_USER')]) {
2    // some block
3}
  • 然后服务器地址,我们也把它写放在流水线里。

  • 修改完sonar-project.properties配置内容后,提交。

image-20230624212603600

 1# 定义唯一的关键字
 2sonar.projectKey=devops6-maven-service
 3
 4# 定义项目名称
 5sonar.projectName=devops6-maven-service
 6
 7# 定义项目的版本信息
 8sonar.projectVersion=1.0
 9 
10# 指定扫描代码的目录位置(多个逗号分隔)
11sonar.sources=.
12 
13# 执行项目编码
14sonar.sourceEncoding=UTF-8
15
16# 指定sonar Server
17# sonar.host.url=http://172.29.9.101:9000
18
19# 认证信息
20# sonar.login=admin
21# sonar.password=Admin@123
22
23# java classes
24sonar.java.binaries=target/classes 
25sonar.java.test.binaries=target/test-classes 
26sonar.java.surefire.report=target/surefire-reports
  • 这里编辑pipeline代码

我们期待的写法是这样的:

image-20230624213624271

  • 继续改写Jenkins pipeline代码
 1@Library("devops06@main") _
 2
 3//import src/org/devops/Build.groovy
 4def build = new org.devops.Build()
 5
 6pipeline {
 7    agent {label "build"}
 8    stages{
 9        stage("CheckOut"){
10            steps{
11                script{
12                    build.CheckOut()
13                }
14            }
15        }
16
17        stage("Build"){
18            steps{
19                script{
20                    build.Build()
21                }
22            }
23
24        }        
25
26        stage("CodeScan"){
27            steps{
28                script{
29
30                    withCredentials([usernamePassword(credentialsId: '003d1667-653e-40f3-8103-b74614643aba', 
31                                    passwordVariable: 'SONAR_PASSWD', 
32                                    usernameVariable: 'SONAR_USER')]) {
33                                        
34                        sh """/data/devops6/sonar-scanner-4.8.0.2856-linux/bin/sonar-scanner \
35                            -Dsonar.login=${SONAR_USER} \
36                            -Dsonar.password=${SONAR_PASSWD} \
37                            -Dsonar.host.url=http://172.29.9.101:9000
38                        """
39                    }
40
41
42                }
43            }
44        }
45
46    }
47}

image-20230624215259682

提交。

  • 运行

image-20230624215316515

image-20230624215232679

可以看到,代码检查步骤也是ok的。

image-20230624215409500

测试结束。😘

💘 实践:Jenkins PipeLine中的代码扫描(测试成功)-2022.5.25

image-20220525215102758

  • 把sonar-project.properties文件提到devops4-maven-service工程里面去:

也就是来到Gitlab的devops4-maven-service项目下,创建一个sonar-project.properties文件,并提交:

image-20220525210706283

 1# 定义项目关键字
 2sonar.projectKey=devop4-maven-service
 3
 4# 定义项目名称
 5sonar.projectName=devops4-maven-service
 6
 7# 定义项目的版本信息
 8sonar.projectVersion=2.0
 9 
10 # 指定扫描代码的目录位置(多个逗号分隔)
11sonar.sources=src
12  
13  # 执行项目编码
14sonar.sourceEncoding=UTF-8
15
16  # 指定sonar Server
17sonar.host.url=http://172.29.9.101:9000
18
19  # 认证信息
20sonar.login=admin
21sonar.password=admin123
22
23sonar.java.binaries=target/classes
  • 先在jenkins里跑一下devops4-maven-service项目流水线,看下是否可正常跑下去:

image-20220525211340725

可以看到能够正常运行流水线。

  • 在jenkins共享库里编写jenkinsfile文件,并提交,然后再次触发流水线:

image-20220525212023657

image-20220525212120228

完整Jenkinsfile代码如下:

 1@Library("mylib@main") _     //加载共享库
 2import org.devops.*						// 导入库
 3
 4def checkout = new Checkout()    //New实例化
 5def build = new Build()
 6def unittest = new UnitTest()
 7
 8//env.buildType = "${JOB_NAME}".split("-")[1]
 9
10//流水线
11pipeline {
12    agent { label "build" }
13
14    options {
15        skipDefaultCheckout true
16    }
17
18    stages{
19        stage("Checkout"){
20            steps{
21                script {
22                    println("GetCode")
23                    checkout.GetCode("${env.srcUrl}", "${env.branchName}")
24                }
25            }
26        }
27
28        stage("Build"){
29            steps{
30                script{
31                    println("Build")
32                    //build.CodeBuild("${env.buildType}")
33                    sh "${env.buildShell}"
34                   
35                }
36            }
37        }
38
39        /*stage("UnitTest"){
40            steps{
41                script{
42                    unittest.CodeTest("${env.buildType}")
43                }
44            }
45        }*/
46
47        stage("CodeScan"){
48            steps{
49                script{
50                    cliPath="/usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/bin"
51                    sh "${cliPath}/sonar-scanner"
52                }
53            }
54        }
55    }
56}
  • 这里优化下,把项目里的sonar-project.properties文件里的sonar.login和sonar.password使用jenkins的凭据功能给隐藏起来:

image-20220525215313708

image-20220525215435863

随便找一个流水线项目,利用流水线语法-片段生成器,来生成代码:

image-20220525215725362

image-20220525215822362

image-20220525215836066

1withCredentials([usernamePassword(credentialsId: '79d8f75b-3733-49f4-950f-19f8480fda03', passwordVariable: 'SONAR_PASSWD', usernameVariable: 'SONAR_USER')]) {
2    // some block
3}
  • 将生成的代码写在jenkins共享库的jenkiinsfile里面,然后再次跑一下流水线,观察效果:
 1@Library("mylib@main") _     //加载共享库
 2import org.devops.*						// 导入库
 3
 4def checkout = new Checkout()    //New实例化
 5def build = new Build()
 6def unittest = new UnitTest()
 7
 8//env.buildType = "${JOB_NAME}".split("-")[1]
 9
10//流水线
11pipeline {
12    agent { label "build" }
13
14    options {
15        skipDefaultCheckout true
16    }
17
18    stages{
19        stage("Checkout"){
20            steps{
21                script {
22                    println("GetCode")
23                    checkout.GetCode("${env.srcUrl}", "${env.branchName}")
24                }
25            }
26        }
27
28        stage("Build"){
29            steps{
30                script{
31                    println("Build")
32                    //build.CodeBuild("${env.buildType}")
33                    sh "${env.buildShell}"
34                   
35                }
36            }
37        }
38
39        /*stage("UnitTest"){
40            steps{
41                script{
42                    unittest.CodeTest("${env.buildType}")
43                }
44            }
45        }*/
46
47        stage("CodeScan"){
48            steps{
49                script{
50                    cliPath="/usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/bin"
51                    withCredentials([usernamePassword(credentialsId: '79d8f75b-3733-49f4-950f-19f8480fda03', 
52                                                                    passwordVariable: 'SONAR_PASSWD', 
53                                                                    usernameVariable: 'SONAR_USER')]) {
54                        // some block
55                        sh """${cliPath}/sonar-scanner  \
56                            -Dsonar.login=${SONAR_USER} \
57                            -Dsonar.password=${SONAR_PASSWD} \
58                            -Dsonar.projectVersion=${env.branchName}
59                        """
60                    }
61                    
62                }
63            }
64        }
65    }
66}

写好后提交:

记得要把devops4-maven-service项目的sonar-project.propertries文件里的相关信息给注释掉:

image-20220526074100772

以上代码都修改完成后,进行提交,再来到jenkins上进行构建。

  • 可以看到,能够正常构建成功:

image-20220526074434831

image-20220526074827725

image-20220526074859189

  • 接下来再进一步扩展:

进一步优化下jenkins共享库里的代码:

创建srg/org/devops/Sonar.groovy文件:

 1package org.devops
 2
 3def CodeScan(branchName){
 4    cliPath="/usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/bin"
 5    withCredentials([usernamePassword(credentialsId: '79d8f75b-3733-49f4-950f-19f8480fda03', 
 6                                                passwordVariable: 'SONAR_PASSWD', 
 7                                                usernameVariable: 'SONAR_USER')]) {
 8        // some block
 9        sh """${cliPath}/sonar-scanner  \
10            -Dsonar.login=${SONAR_USER} \
11            -Dsonar.password=${SONAR_PASSWD} \
12            -Dsonar.projectVersion=${branchName}
13        """
14    }
15}

编写Jenkinsfile里的代码:

 1@Library("mylib@main") _     //加载共享库
 2import org.devops.*						// 导入库
 3
 4def checkout = new Checkout()    //New实例化
 5def build = new Build()
 6def unittest = new UnitTest()
 7def sonar = new Sonar() 
 8
 9//env.buildType = "${JOB_NAME}".split("-")[1]
10
11//流水线
12pipeline {
13    agent { label "build" }
14
15    options {
16        skipDefaultCheckout true
17    }
18
19    stages{
20        stage("Checkout"){
21            steps{
22                script {
23                    println("GetCode")
24                    checkout.GetCode("${env.srcUrl}", "${env.branchName}")
25                }
26            }
27        }
28
29        stage("Build"){
30            steps{
31                script{
32                    println("Build")
33                    //build.CodeBuild("${env.buildType}")
34                    sh "${env.buildShell}"
35                   
36                }
37            }
38        }
39
40        /*stage("UnitTest"){
41            steps{
42                script{
43                    unittest.CodeTest("${env.buildType}")
44                }
45            }
46        }*/
47
48        stage("CodeScan"){
49            steps{
50                script{
51                    sonar.CodeScan("${env.branchName}")
52                    }
53                    
54                }
55        }
56        
57    }
58}

修改完成后,提交并构建:

image-20220526093650516

image-20220526093717054

可以看到,可以成功构建!😘

(2)使用Jenkins扩展插件的方式

💘 实践:Jenkins集成SonarQube(Jenkins插件方式)(测试成功)-2023.6.24
  • 实验环境
1jenkins/jenkins:2.346.3-2-lts-jdk11
2gitlab/gitlab-ce:15.0.3-ce.0
3sonarqube:9.9.0-community
4SonarScanner 4.8.0.2856
  • 实验软件

链接:https://pan.baidu.com/s/1TWnlWTaHP_XP3O1aErXCdA?pwd=0820 提取码:0820 2023.6.24-Jenkins集成SonarQube-code

image-20230624223946529

  • 官方文档

参考:https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-jenkins/

image-20230624215746582

  • 创建SonaQube的账户token
1sqa_1339c18e86d442f9209bf363376554d5df5ca7a5

我的账号-生成令牌。

image-20230624215859394

  • 将token保存到Jenkins凭据中

image-20230624220323575

image-20230624220415524

image-20230624220426170

生成代码:

image-20230624222545276

1withSonarQubeEnv(credentialsId: 'bdefe5e2-62a0-4e20-bf5a-51abedfe6df1') {
2    // some block
3}
  • 在Jenkins中安装插件sonarqube scanner。

image-20230624220042729

  • 转到"管理Jenkins>系统配置",向下滚动到SonarQube配置部分,单击Add SonarQube,添加服务器,选择凭据。

image-20230624222054199

在片段生成器中查看用法, 注入与所选SonarQube 安装相关的环境变量。将设置以下变量:

1SONAR_HOST_URL     ## 在jenkins管理页面配置的sonar地址
2SONAR_AUTH_TOKEN   ## 在jenkins管理页面配置的sonar认证信息
  • 此时,继续编写pipeline代码

使用withSonarQubeEnv DSL引入在Jenkins中配置的sonar环境。

 1@Library("devops06@main") _
 2
 3//import src/org/devops/Build.groovy
 4def build = new org.devops.Build()
 5
 6pipeline {
 7    agent {label "build"}
 8    stages{
 9        stage("CheckOut"){
10            steps{
11                script{
12                    build.CheckOut()
13                }
14            }
15        }
16
17        stage("Build"){
18            steps{
19                script{
20                    build.Build()
21                }
22            }
23
24        }        
25
26        stage("CodeScan"){
27            steps{
28                script{
29
30                    // withCredentials([usernamePassword(credentialsId: '003d1667-653e-40f3-8103-b74614643aba', 
31                    //                 passwordVariable: 'SONAR_PASSWD', 
32                    //                 usernameVariable: 'SONAR_USER')]) {
33                                        
34                    //     sh """/data/devops6/sonar-scanner-4.8.0.2856-linux/bin/sonar-scanner \
35                    //         -Dsonar.login=${SONAR_USER} \
36                    //         -Dsonar.password=${SONAR_PASSWD} \
37                    //         -Dsonar.host.url=http://172.29.9.101:9000
38                    //     """
39                    // }
40
41
42                    withSonarQubeEnv(credentialsId: 'bdefe5e2-62a0-4e20-bf5a-51abedfe6df1') {
43                        sh """/data/devops6/sonar-scanner-4.8.0.2856-linux/bin/sonar-scanner \
44                        -Dsonar.login=${SONAR_AUTH_TOKEN} \
45                        -Dsonar.host.url=${SONAR_HOST_URL}
46                        """
47                    }
48
49                }
50            }
51        }
52
53    }
54}

image-20230624222916967

  • 跑流水线

用了插件的好处:这里可以直接跳过去,看报告。

最终的效果如下:

相比命令行只是增加了一些扩展链接, 对于经常操作Jenkins的用户方便些。

image-20230624222827897

image-20230624222848378

测试成功。😘

2.GitLAB CI集成

💘 实践:GitLab PipeLine中的代码扫描(测试成功)-2022.5.26

image-20220526100326612

  • 使用FROUP Variable存储sonar的账号和密码:

来到devops4组下:

image-20220526101403551

image-20220526101432510

image-20220526101459793

  • 来到devops4-gitlablib-service项目里:

编辑jobs/CI.yaml文件:

image-20220526131531505

 1.pipelineInit:
 2  tags:
 3    - "${RUNNER_TAG}"
 4  stage: .pre
 5  variables:
 6    GIT_CHECKOUT: "true"   ##局部开启作业的代码下载
 7  script:
 8    - ls -l 
 9
10.cibuild:
11  tags:
12    - "${RUNNER_TAG}"
13  stage: build
14  script:
15    - echo "${BUILD_SHELL}"
16    - ${BUILD_SHELL}
17  # artifacts:
18  #    paths:
19  #     - ${ARTIFACT_PATH}
20
21.citest:
22  tags:
23    - "${RUNNER_TAG}"
24  stage: test
25  script:
26    - echo "${TEST_SHELL}"
27    - ${TEST_SHELL}
28  # artifacts:
29  #   reports:
30  #     junit: ${TEST_REPORTS}
31
32.codescan:
33  tags: 
34    - "${RUNNER_TAG}"
35  stage: codescan
36  script:
37    |-
38      /usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/bin/sonar-scanner \
39      -Dsonar.login=${SONAR_USER} \
40      -Dsonar.password=${SONAR_PASSWD} \
41      -Dsonar.projectVersion=${CI_COMMIT_BRANCH}

编辑.gitlabci.yml文件:

image-20220526131503593

 1include:
 2  - project: 'devops4/devops4-gitlablib-service'
 3    ref: main
 4    file: 
 5      - '/jobs/CI.yaml'
 6
 7workflow:
 8  rules:
 9    - if: $CI_PIPELINE_SOURCE == "web"
10      when: always
11    - if: $CI_COMMIT_BEFORE_SHA == "0000000000000000000000000000000000000000"
12      when: never
13    - when: always
14
15variables:
16  GIT_CHECKOUT: "false"   ## 全局关闭作业代码下载
17  BUILD_SHELL: "sh -x build.sh"  ## 构建命令
18  TEST_SHELL: "/usr/local/apache-maven-3.8.5/bin/mvn test "                        ## 测试命令
19  # ARTIFACT_PATH: "target/*jar"      ## 制品路径  
20  # TEST_REPORTS: "target/surefire-reports/TEST-*.xml" ##测试报告
21  RUNNER_TAG: "builder"
22
23stages:
24  - build
25  - test
26  - codescan
27
28pipelineInit:
29  extends: 
30    - .pipelineInit
31
32cibuild:
33  extends:
34    - .cibuild
35
36citest:
37  extends:
38    - .citest
39
40codescan:
41  extends:
42  - .codescan
  • 编写完成后,提交,然后就可以看到能够成功构建:

image-20220526130748413

image-20220526130812788

image-20220526130914329

测试完成。😘

3.插件方式

💘 实践:Jenkins插件进行扫描(测试成功)-2022.5.26

image-20220526143119536

image-20220526143142333

  • 官方参考

参考:https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-jenkins/

image-20220526143355871

  • 创建SonaQube的账户token:

image-20220526143651893

  • 将token保存到Jenkins凭据中:

image-20220526144137293

  • 在Jenkins中安装插件sonarqube scanner:

image-20220526144256576

  • 转到"管理Jenkins>系统配置",向下滚动到SonarQube配置部分,单击Add SonarQube,添加服务器,选择凭据。

image-20220526145056292

  • 在片段生成器里生成代码:

使用withSonarQubeEnv DSL引入在Jenkins中配置的sonar环境:

image-20220526145311572

1withSonarQubeEnv(credentialsId: '09a996dd-2b05-46c8-90f9-09905bbc22c9') {
2    // some block
3}
  • 然后在之前的devops4-maven-service回放里编写下代码:
 1@Library("mylib@main") _     //加载共享库
 2import org.devops.*                     // 导入库
 3
 4def checkout = new Checkout()    //New实例化
 5def build = new Build()
 6def unittest = new UnitTest()
 7def sonar = new Sonar()
 8
 9//env.buildType = "${JOB_NAME}".split("-")[1]
10
11//流水线
12pipeline {
13    agent { label "build" }
14
15    options {
16        skipDefaultCheckout true
17    }
18
19    stages{
20        stage("Checkout"){
21            steps{
22                script {
23                    println("GetCode")
24                    checkout.GetCode("${env.srcUrl}", "${env.branchName}")
25                }
26            }
27        }
28
29        stage("Build"){
30            steps{
31                script{
32                    println("Build")
33                    //build.CodeBuild("${env.buildType}")
34                    sh "${env.buildShell}"
35                   
36                }
37            }
38        }
39
40        /*stage("UnitTest"){
41            steps{
42                script{
43                    unittest.CodeTest("${env.buildType}")
44                }
45            }
46        }*/
47
48        stage("CodeScan"){
49            steps{
50                script{
51                   
52                    withSonarQubeEnv(credentialsId: '09a996dd-2b05-46c8-90f9-09905bbc22c9') {
53                        cliPath="/usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/bin"
54                        sh """${cliPath}/sonar-scanner  \
55                                -Dsonar.projectVersion=master
56                        """
57                    }
58                }
59            }
60        
61        }
62    }
63}
  • 运行并观察效果:

相比命令行只是增加了一些扩展链接, 对于经常操作Jenkins的用户方便些。

image-20220526151220602

image-20220526151325838

image-20220526151343047

测试完成。😘

5、SonarQube REST API实践(可选)

💘 实践:SonarQube REST API(测试成功)-2022.5.27

SonarQube系统的API文档: http://172.29.9.101:9000/web_api

 1//查找项目
 2api/projects/search?projects=${projectName}"
 3
 4//创建项目
 5api/projects/create?name=${projectName}&project=${projectName}"
 6   
 7//更新语言规则集
 8api/qualityprofiles/add_project?language=${language}&qualityProfile=${qualityProfile}&project=${projectName}"
 9
10//项目授权
11api/permissions/apply_template?projectKey=${projectKey}&templateName=${templateName}"
12
13//更新质量阈
14api/qualitygates/select?projectKey=${projectKey}&gateId=${gateId}"

image-20220527093552171

SonarQube API的请求方法

1curl --location \
2--request GET \
3'http://192.168.1.200:9000/api/projects/search?projects=day4-maven2-service' \
4--header 'Authorization: Basic YWRtaW46YWRtaW4xMjM0'

jenkins代码中不要存在敏感信息, 将base64格式的SonarQube 用户token YWRtaW46YWRtaW4xMjM0存储到Jenkins凭据中(Secret Text类型),后续使用withCredentials将值赋值给变量SONAR_TOKEN

考虑到Api的URL都具有相同部分http://192.168.1.200:9000/api所以单独复制给变量sonarApi。每个接口返回的都是JSON类型的数据, 这里使用readJSON进行解析和处理。【所以有了下面的代码】

 1def SonarRequest(apiUrl,method){
 2    withCredentials([string(credentialsId: "52df4ad9-7167-4bf6-a1fc-2f9f17713472", variable: 'SONAR_TOKEN')]) {
 3        sonarApi = "http://192.168.1.200:9000/api"
 4        response = sh  returnStdout: true, 
 5            script: """
 6            curl --location \
 7                 --request ${method} \
 8                 "${sonarApi}/${apiUrl}" \
 9                 --header "Authorization: Basic ${SONAR_TOKEN}"
10            """
11        try {
12            response = readJSON text: """ ${response - "\n"} """
13        } catch(e){
14            response = readJSON text: """{"errors" : true}"""
15        }
16        return response
17        
18    }
19}

1.查找项目

接口地址和参数: http://172.29.9.101:9000/api/projects/search?projects=day4-maven-service

请求类型: GET

  • postman调试:

image-20220527100250862

image-20220527100324390

转换cURL命令:

1curl --location --request GET 'http://172.29.9.101:9000/api/projects/search?projects=devops4-maven-service' \
2--header 'Authorization: Basic YWRtaW46YWRtaW4xMjM='

image-20220527100547369

  • Jenkins代码中不要存在敏感信息, 将base64格式的SonarQube 用户token YWRtaW46YWRtaW4xMjM0存储到Jenkins凭据中(Secret Text类型),后续使用withCredentials将值赋值给变量SONAR_TOKEN

在jenkins里添加凭据:

image-20220527100852036

利用片段式语法生成代码:

image-20220527101034209

  • 编写jenkins pipeline代码,然后找一个pipeline项目的回放里进行测试:
 1pipeline {
 2	agent {
 3		label "build"
 4	}
 5
 6	stages {
 7		stage("run"){
 8			steps{
 9				script{
10					result = ProjectSearch("devops4-maven-service")
11					println(result)
12				}
13			}
14		}
15	}
16}
17
18// 查找项目
19def ProjectSearch(projectName){
20    apiUrl = "projects/search?projects=${projectName}"
21    response = SonarRequest(apiUrl,"GET")
22
23    if (response.paging.total == 0){
24        println("Project not found!.....")
25        return false
26    } 
27    return true
28}
29
30
31def SonarRequest(apiUrl,method){
32    withCredentials([string(credentialsId: "66b7dd41-5434-43a3-8d02-505e2f2dc38f", variable: 'SONAR_TOKEN')]) {
33        sonarApi = "http://172.29.9.101:9000/api"
34        response = sh  returnStdout: true, 
35            script: """
36            curl --location \
37                 --request ${method} \
38                 "${sonarApi}/${apiUrl}" \
39                 --header "Authorization: Basic ${SONAR_TOKEN}"
40            """
41        try {
42            response = readJSON text: """ ${response - "\n"} """
43        } catch(e){
44            response = readJSON text: """{"errors" : true}"""
45        }
46        return response
47        
48    }
49}

运行测试:

image-20220527101203978

image-20220527101222620

  • 此时,我先把刚才sonar里面的项目给删除掉,再来跑一次流水线,看下效果:

image-20220527101544875

可以看到,会提示项目不存在。

2.创建项目

接口地址和参数: http://172.29.9.101:9000/api/projects/create?name=devops4-maven-service&project=devops4-maven-service

请求类型:POST

  • postman调试:

image-20220527103443171

cURL:

1curl --location --request POST 'http://172.29.9.101:9000/api/projects/create?name=devops4-maven-service&project=devops4-maven-service' \
2--header 'Authorization: Basic YWRtaW46YWRtaW4xMjM='
  • 编写Jenkins Pipeline,并运行测试:
 1// 创建项目
 2def CreateProject(projectName){
 3    apiUrl = "projects/create?name=${projectName}&project=${projectName}"
 4    response = SonarRequest(apiUrl,"POST")
 5    try{
 6        if (response.project.key == projectName ) {
 7            println("Project Create success!...")
 8            return true
 9        }
10    }catch(e){
11        println(response.errors)
12        return false
13    }
14}

完整代码如下:

 1pipeline {
 2	agent {
 3		label "build"
 4	}
 5
 6	stages {
 7		stage("run"){
 8			steps{
 9				script{
10					projectName = "devops4-maven-service"
11					result = ProjectSearch("devops4-maven-service")
12					println(result)
13
14					if (result == false){
15						CreateProject(projectName)
16					}
17				}
18			}
19		}
20	}
21}
22
23// 查找项目
24def ProjectSearch(projectName){
25    apiUrl = "projects/search?projects=${projectName}"
26    response = SonarRequest(apiUrl,"GET")
27
28    if (response.paging.total == 0){
29        println("Project not found!.....")
30        return false
31    } 
32    return true
33}
34
35
36def SonarRequest(apiUrl,method){
37    withCredentials([string(credentialsId: "66b7dd41-5434-43a3-8d02-505e2f2dc38f", variable: 'SONAR_TOKEN')]) {
38        sonarApi = "http://172.29.9.101:9000/api"
39        response = sh  returnStdout: true, 
40            script: """
41            curl --location \
42                 --request ${method} \
43                 "${sonarApi}/${apiUrl}" \
44                 --header "Authorization: Basic ${SONAR_TOKEN}"
45            """
46        try {
47            response = readJSON text: """ ${response - "\n"} """
48        } catch(e){
49            response = readJSON text: """{"errors" : true}"""
50        }
51        return response
52        
53    }
54}
55
56// 创建项目
57def CreateProject(projectName){
58    apiUrl = "projects/create?name=${projectName}&project=${projectName}"
59    response = SonarRequest(apiUrl,"POST")
60    try{
61        if (response.project.key == projectName ) {
62            println("Project Create success!...")
63            return true
64        }
65    }catch(e){
66        println(response.errors)
67        return false
68    }
69}

image-20220527103838398

image-20220527103851613

可以看到,sonar上项目被创建成功!

3.更新项目质量配置

接口地址和参数: http://172.29.9.101:9000/api/qualityprofiles/add_project?language=java&project=devops4-maven-service&qualityProfile=devop4

请求类型:POST

postman调试:

image-20220527143433663

cUrl:

1curl --location --request POST 'http://172.29.9.101:9000/api/qualityprofiles/add_project?language=java&project=devops4-maven-service&qualityProfile=devops4' \
2--header 'Authorization: Basic YWRtaW46YWRtaW4xMjM='

Jenkins Pipeline:

 1// 更新项目质量配置
 2def UpdateQualityProfiles(lang, projectName, profileName){
 3    apiUrl = "qualityprofiles/add_project?language=${lang}&project=${projectName}&qualityProfile=${profileName}"
 4    response = SonarRequest(apiUrl,"POST")
 5    
 6    if (response.errors != true){
 7        println("ERROR: UpdateQualityProfiles ${response.errors}...")
 8        return false
 9    } else {
10        println("SUCCESS: UpdateQualityProfiles ${lang} > ${projectName} > ${profileName}" )
11        return true
12    }
13}

完整代码如下:

 1pipeline {
 2	agent {
 3		label "build"
 4	}
 5
 6	stages {
 7		stage("run"){
 8			steps{
 9				script{
10					projectName = "devops4-maven-service"
11					result = ProjectSearch("devops4-maven-service")
12					println(result)
13
14					if (result == false){
15						CreateProject(projectName)
16					}
17
18					UpdateQualityProfiles("java", projectName, "devops4")
19				}
20			}
21		}
22	}
23}
24
25// 查找项目
26def ProjectSearch(projectName){
27    apiUrl = "projects/search?projects=${projectName}"
28    response = SonarRequest(apiUrl,"GET")
29
30    if (response.paging.total == 0){
31        println("Project not found!.....")
32        return false
33    } 
34    return true
35}
36
37
38def SonarRequest(apiUrl,method){
39    withCredentials([string(credentialsId: "66b7dd41-5434-43a3-8d02-505e2f2dc38f", variable: 'SONAR_TOKEN')]) {
40        sonarApi = "http://172.29.9.101:9000/api"
41        response = sh  returnStdout: true, 
42            script: """
43            curl --location \
44                 --request ${method} \
45                 "${sonarApi}/${apiUrl}" \
46                 --header "Authorization: Basic ${SONAR_TOKEN}"
47            """
48        try {
49            response = readJSON text: """ ${response - "\n"} """
50        } catch(e){
51            response = readJSON text: """{"errors" : true}"""
52        }
53        return response
54        
55    }
56}
57
58// 创建项目
59def CreateProject(projectName){
60    apiUrl = "projects/create?name=${projectName}&project=${projectName}"
61    response = SonarRequest(apiUrl,"POST")
62    try{
63        if (response.project.key == projectName ) {
64            println("Project Create success!...")
65            return true
66        }
67    }catch(e){
68        println(response.errors)
69        return false
70    }
71}
72
73// 更新项目质量配置
74def UpdateQualityProfiles(lang, projectName, profileName){
75    apiUrl = "qualityprofiles/add_project?language=${lang}&project=${projectName}&qualityProfile=${profileName}"
76    response = SonarRequest(apiUrl,"POST")
77    
78    if (response.errors != true){
79        println("ERROR: UpdateQualityProfiles ${response.errors}...")
80        return false
81    } else {
82        println("SUCCESS: UpdateQualityProfiles ${lang} > ${projectName} > ${profileName}" )
83        return true
84    }
85}

运行后效果:

image-20220527144057623

image-20220527144159867

4.控制逻辑(实践)

  • 项目没有配置质量, 默认使用sonarway(内置质量)。

  • 默认直接使用sonarscanner,扫描的项目,使用的内置的默认质量。

如果具有单独的质量配置,例如每个组织一个质量配置。 此时就需要先手动在web页面创建一个空的项目,然后在项目的配置中设置目标质量配置。(下面是手动的操作步骤)

image-20220527150812463

image-20220527150829647

image-20220527150850351

image-20220527150911189

自动化实现方式 :

  • 查询项目是否存在

    • 存在: 直接更新质量配置
    • 不存在: 创建空的项目然后更新质量配置
 1stage("sonartest"){
 2            steps{
 3                script{
 4
 5                    // 判断项目是否存在
 6                    sonarProjectName = "${JOB_NAME.split('/')[-1]}"
 7                    result = ProjectSearch(sonarProjectName)
 8                    println(result)
 9
10                    if (result != true){
11                        println("Create Sonar Project!...")
12                        CreateProject(sonarProjectName)
13                    }
14
15                    // 指定项目的配置
16                    UpdateQualityProfiles("java", sonarProjectName, "${sonarProjectName.split('-')[0]}")
17                }
18            }
19        }

完整的jenkinsfile代码:

 1pipeline {
 2
 3    agent {
 4        label "build"
 5    }
 6
 7    stages{
 8        stage("sonartest"){
 9            steps{
10                script{
11
12                    // 判断项目是否存在
13                    sonarProjectName = "${JOB_NAME.split('/')[-1]}"
14                    result = ProjectSearch(sonarProjectName)
15                    println(result)
16
17                    if (result != true){
18                        println("Create Sonar Project!...")
19                        CreateProject(sonarProjectName)
20                    }
21
22                    // 指定项目的配置
23                    UpdateQualityProfiles("java", sonarProjectName, "${sonarProjectName.split('-')[0]}")
24                }
25            }
26        }
27    }
28}
29
30def SonarRequest(apiUrl,method){
31    withCredentials([string(credentialsId: "52df4ad9-7167-4bf6-a1fc-2f9f17713472", variable: 'SONAR_TOKEN')]) {
32        sonarApi = "http://192.168.1.200:9000/api"
33        response = sh  returnStdout: true, 
34            script: """
35            curl --location \
36                 --request ${method} \
37                 "${sonarApi}/${apiUrl}" \
38                 --header "Authorization: Basic ${SONAR_TOKEN}"
39            """
40        try {
41            response = readJSON text: """ ${response - "\n"} """
42        } catch(e){
43            response = readJSON text: """{"errors" : true}"""
44        }
45        return response
46        
47    }
48}
49
50// 更新质量阈
51def UpdateQualityProfiles(lang, projectName, profileName){
52    apiUrl = "qualityprofiles/add_project?language=${lang}&project=${projectName}&qualityProfile=${profileName}"
53    response = SonarRequest(apiUrl,"POST")
54    
55    if (response.errors != true){
56        println("ERROR: UpdateQualityProfiles ${response.errors}...")
57        return false
58    } else {
59        println("SUCCESS: UpdateQualityProfiles ${lang} > ${projectName} > ${profileName}" )
60        return true
61    }
62}
63
64// 创建项目
65def CreateProject(projectName){
66    apiUrl = "projects/create?name=${projectName}&project=${projectName}"
67    response = SonarRequest(apiUrl,"POST")
68    try{
69        if (response.project.key == projectName ) {
70            println("Project Create success!...")
71            return true
72        }
73    }catch(e){
74        println(response.errors)
75        return false
76    }
77}
78
79// 查找项目
80def ProjectSearch(projectName){
81    apiUrl = "projects/search?projects=${projectName}"
82    response = SonarRequest(apiUrl,"GET")
83
84    if (response.paging.total == 0){
85        println("Project not found!.....")
86        return false
87    } 
88    return true
89}

🍀 自己实际测试过程如下:

  • 来到jenkins共享库devops4-jenkinslib-service,我们创在Sonar.grovy文件里进行编辑:

image-20220527200631422

 1package org.devops
 2
 3def CodeScan(branchName){
 4    cliPath="/usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/bin"
 5    withCredentials([usernamePassword(credentialsId: '79d8f75b-3733-49f4-950f-19f8480fda03', 
 6                                                passwordVariable: 'SONAR_PASSWD', 
 7                                                usernameVariable: 'SONAR_USER')]) {
 8        // some block
 9        sh """${cliPath}/sonar-scanner  \
10            -Dsonar.login=${SONAR_USER} \
11            -Dsonar.password=${SONAR_PASSWD} \
12            -Dsonar.projectVersion=${branchName}
13        """
14    }
15}
16
17def Init(projectName, lang, profileName){
18    result = ProjectSearch(projectName)
19    println(result)
20
21    if (result == false){
22        CreateProject(projectName)
23    }
24
25    UpdateQualityProfiles(lang, projectName, profileName)
26}
27
28
29
30// 查找项目
31def ProjectSearch(projectName){
32    apiUrl = "projects/search?projects=${projectName}"
33    response = SonarRequest(apiUrl,"GET")
34
35    if (response.paging.total == 0){
36        println("Project not found!.....")
37        return false
38    } 
39    return true
40}
41
42
43def SonarRequest(apiUrl,method){
44    withCredentials([string(credentialsId: "66b7dd41-5434-43a3-8d02-505e2f2dc38f", variable: 'SONAR_TOKEN')]) {
45        sonarApi = "http://172.29.9.101:9000/api"
46        response = sh  returnStdout: true, 
47            script: """
48            curl --location \
49                 --request ${method} \
50                 "${sonarApi}/${apiUrl}" \
51                 --header "Authorization: Basic ${SONAR_TOKEN}"
52            """
53        try {
54            response = readJSON text: """ ${response - "\n"} """
55        } catch(e){
56            response = readJSON text: """{"errors" : true}"""
57        }
58        return response
59        
60    }
61}
62
63// 创建项目
64def CreateProject(projectName){
65    apiUrl = "projects/create?name=${projectName}&project=${projectName}"
66    response = SonarRequest(apiUrl,"POST")
67    try{
68        if (response.project.key == projectName ) {
69            println("Project Create success!...")
70            return true
71        }
72    }catch(e){
73        println(response.errors)
74        return false
75    }
76}
77
78// 更新项目质量配置
79def UpdateQualityProfiles(lang, projectName, profileName){
80    apiUrl = "qualityprofiles/add_project?language=${lang}&project=${projectName}&qualityProfile=${profileName}"
81    response = SonarRequest(apiUrl,"POST")
82    
83    if (response.errors != true){
84        println("ERROR: UpdateQualityProfiles ${response.errors}...")
85        return false
86    } else {
87        println("SUCCESS: UpdateQualityProfiles ${lang} > ${projectName} > ${profileName}" )
88        return true
89    }
90}

jenkinsfile代码:

image-20220527161544699

 1@Library("mylib@main") _     //加载共享库
 2import org.devops.*						// 导入库
 3
 4def checkout = new Checkout()    //New实例化
 5def build = new Build()
 6def unittest = new UnitTest()
 7def sonar = new Sonar()
 8
 9//env.buildType = "${JOB_NAME}".split("-")[1]
10
11//流水线
12pipeline {
13    agent { label "build" }
14
15    options {
16        skipDefaultCheckout true
17    }
18
19    stages{
20        stage("Checkout"){
21            steps{
22                script {
23                    println("GetCode")
24                    checkout.GetCode("${env.srcUrl}", "${env.branchName}")
25                }
26            }
27        }
28
29        stage("Build"){
30            steps{
31                script{
32                    println("Build")
33                    //build.CodeBuild("${env.buildType}")
34                    sh "${env.buildShell}"
35                   
36                }
37            }
38        }
39
40        /*stage("UnitTest"){
41            steps{
42                script{
43                    unittest.CodeTest("${env.buildType}")
44                }
45            }
46        }*/
47
48        stage("CodeScan"){
49            steps{
50                script{
51                    profileName = "${JOB_NAME}".split("-")[0]
52                    sonar.Init("${JOB_NAME}", "java", profileName)
53                    sonar.CodeScan("${env.branchName}")
54                    }
55                    
56                }
57        }
58        
59    }
60}

注意:也要保证devops4-maven-service项目里sonar-project.properties文件的配置正确:

image-20220527200544085

  • 提交流水线,观察效果:

image-20220527200527645

image-20220527200453554

image-20220527200437075

符合预期。😘

6、其他日常使用实践

image-20230625002555845

1.规则的禁用与启用

目的: 掌握默认规则中的一部分规则如何激活和禁用。

质量规则、质量阈–这一般是由TeamLeader、开发人员去指定的。

进入质量配置页面, 可以看到所有的语言规则配置。在这里可以看到规则的使用情况。

image-20220526163338496

这里假设我要调整Go语言的规则配置, 点击规则数量数字。

image-20220526163411808

image-20220526163432210

创建新的规则集:

image-20220526163507119

点击更多激活规则, 进入规则设置页面。

image-20220526163534952

激活或者下线规则。(活动/挂起)

image-20220526163558278

使用规则: 先在页面配置项目,然后使用SonarScanner扫描。

image-20220526163638026

💘 实践:新建SonarQube质量配置(测试成功)-2023.6.25

自己测试过程:

  • 测试环境
1sonarqube:9.9.0-community
  • 创建新的质量配置

image-20230625061319404

  • 给特定的配置规则指定项目

image-20230625061514779

image-20230625061521958

  • 激活更多规则

image-20230625061716716

image-20230625061748202

image-20230625061807466

完成。

2.质量阈的配置

目的: 适用于以质量门禁作为交付关卡。

image-20220526164453427

image-20220526164523681

💘 实践:新建SonarQube质量阈(测试成功)-2023.6.25

自己测试过程:

  • 测试环境
1sonarqube:9.9.0-community
  • 创建新的质量阈

image-20230625062234005

  • 给新创建的质量阈指定项目

image-20230625062329756

  • 也可以添加新的条件

image-20230625062459190

3.代码覆盖率统计

找一个具有大量单元测试的项目, 然后集成jacoco插件,生成覆盖率报告,最后由sonar收集。

  • Maven集成Jacoco(本次在gitlab的devops4-maven-service项目里的pom.xml里修改)

添加jacoco-maven-pluginjunit插件。

 1<dependencies>
 2    <dependency>
 3        <groupId>org.jacoco</groupId>
 4        <artifactId>jacoco-maven-plugin</artifactId>
 5        <version>0.8.2</version>
 6        <scope>test</scope>
 7    </dependency>
 8    <dependency>
 9        <groupId>junit</groupId>
10        <artifactId>junit</artifactId>
11        <version>4.12</version>
12        <scope>test</scope>
13    </dependency>
14</dependencies>

添加插件:

 1<plugin>
 2    <groupId>org.apache.maven.plugins</groupId>
 3	<artifactId>maven-compiler-plugin</artifactId>
 4	<version>3.6.1</version>
 5	<configuration>
 6    	<skipMain>true</skipMain>
 7		<skip>true</skip>
 8		<source>1.8</source>
 9		<target>1.8</target>
10	</configuration>
11</plugin>
12
13
14<plugin>
15  <groupId>org.jacoco</groupId>
16  <artifactId>jacoco-maven-plugin</artifactId>
17  <version>0.7.5.201505241946</version>
18  <executions>
19    <execution>
20      <id>prepare-agent</id>
21      <goals>
22        <goal>prepare-agent</goal>
23      </goals>
24    </execution>
25    <execution>
26      <id>report</id>
27      <phase>prepare-package</phase>
28      <goals>
29        <goal>report</goal>
30      </goals>
31    </execution>
32    <execution>
33      <id>post-unit-test</id>
34      <phase>test</phase>
35      <goals>
36        <goal>report</goal>
37      </goals>
38      <configuration>
39        <dataFile>target/jacoco.exec</dataFile>
40        <outputDirectory>target/jacoco-reports</outputDirectory>
41      </configuration>
42    </execution>
43  </executions>
44  <configuration>
45    <systemPropertyVariables>
46      <jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
47    </systemPropertyVariables>
48  </configuration>
49</plugin>
  • SonarQube安装Jacoco插件(8.9.1 版本可以跳过,已经集成

https://github.com/SonarSource/sonar-jacoco/releases/download/1.1.0.898/sonar-jacoco-plugin-1.1.0.898.jar

image-20220526193551442

1# 指定代码覆盖率工具为jacoco
2sonar.core.codeCoveragePlugin=jacoco
3# 指定exec二进制文件存放路径
4sonar.jacoco.reportPaths=target/jacoco.exec
 1cd devops-jacoco-service/
 2sonar-scanner -Dsonar.host.url=http://192.168.1.200:9000 \
 3-Dsonar.projectKey=devops-jacoco-service \
 4-Dsonar.projectName=devops-jacoco-service \
 5-Dsonar.projectVersion=1.0 \
 6-Dsonar.login=admin \
 7-Dsonar.password=admin \
 8-Dsonar.ws.timeout=30 \
 9-Dsonar.projectDescription="my first project!" \
10-Dsonar.links.homepage=http://www.baidu.com \
11-Dsonar.sources=src \
12-Dsonar.sourceEncoding=UTF-8 \
13-Dsonar.java.binaries=target/classes \
14-Dsonar.java.test.binaries=target/test-classes \
15-Dsonar.java.surefire.report=target/surefire-reports \
16-Dsonar.core.codeCoveragePlugin=jacoco \
17-Dsonar.jacoco.reportPaths=target/jacoco.exec
  • 以上编辑完成后,提交,然后再jenkins上构建验证:

image-20220526195904254

image-20220526200141381

image-20220526200304214

==测试效果:(失败)==—老师当时这里也是没做实际测试的!

测试时间:2023年6月25日

测试环境:

1apache-maven-3.9.2
2jenkins/jenkins:2.346.3-2-lts-jdk11
3gitlab/gitlab-ce:15.0.3-ce.0
4sonarqube:9.9.0-community
5SonarScanner 4.8.0.2856

当前maven版本无jacoco插件:

image-20230625070317135

构建失败:

image-20230625070530207

image-20230625070632543

4.多分支代码扫描

https://github.com/mc1arke/sonarqube-community-branch-plugin/releases

image-20220528092619465

1. Sonar 7.9.6版本

将插件放到extensions/pluginslib/common目录中,然后重启sonar。

 1## 临时方案
 2docker exec -it sonarqube bash
 3cd /opt/sonarqube/lib/common
 4cp ../../extensions/plugins/sonarqube-community-branch-plugin-1.3.2 ./
 5exit
 6
 7docker restart sonarqube 
 8
 9
10## 持久化lib目录后
11[root@zeyang-nuc-service sonarqube]# ls
12sonarqube_conf  sonarqube_data  sonarqube_extensions  sonarqube_lib  sonarqube_logs
13[root@zeyang-nuc-service sonarqube]# cp /root/sonarqube-community-branch-plugin-1.3.2.jar  sonarqube_extensions/plugins/
14[root@zeyang-nuc-service sonarqube]# chmod +x sonarqube_extensions/plugins/sonarqube-community-branch-plugin-1.3.2.jar
15[root@zeyang-nuc-service sonarqube]#
16[root@zeyang-nuc-service sonarqube]#
17[root@zeyang-nuc-service sonarqube]# cp /root/sonarqube-community-branch-plugin-1.3.2.jar  sonarqube_lib/common/
18[root@zeyang-nuc-service sonarqube]# chmod +x sonarqube_lib/common/sonarqube-community-branch-plugin-1.3.2.jar
19[root@zeyang-nuc-service sonarqube]#
20
21
22docker restart sonarqube

image-20220528092757954

 1##扫描参数增加 –Dsonar.branch.name=
 2
 3sonar-scanner -Dsonar.host.url=http://192.168.1.200:9000 \
 4-Dsonar.projectKey=devops-maven2-service \
 5-Dsonar.projectName=devops-maven2-service \
 6-Dsonar.projectVersion=1.0 \
 7-Dsonar.login=admin \
 8-Dsonar.password=admin \
 9-Dsonar.ws.timeout=30 \
10-Dsonar.projectDescription="my first project!" \
11-Dsonar.links.homepage=http://192.168.1.200/devops/devops-maven-service \
12-Dsonar.links.ci=http://192.168.1.200:8080/job/demo-pipeline-service/ \
13-Dsonar.sources=src \
14-Dsonar.sourceEncoding=UTF-8 \
15-Dsonar.java.binaries=target/classes \
16-Dsonar.java.test.binaries=target/test-classes \
17-Dsonar.java.surefire.report=target/surefire-reports \
18-Dsonar.branch.name=release-1.1.1

⚠️ 注意: 需要先把主分支扫描一遍,不然会报错。

1ERROR: Error during SonarScanner execution
2ERROR: No branches currently exist in this project. Please scan the main branch without passing any branch parameters.
3ERROR:
4ERROR: Re-run SonarScanner using the -X switch to enable full debug logging.

2. Sonar 8.9.1 版本

新版本插件的配置有变化,效果和使用方式不变。

  1. 将插件下载到extensions/plugins/目录。
  2. 更新sonar服务端的配置文件。
  3. 重启docker restart sonarqube 。
 1# cd /data/cicd2/sonarqube/
 2# ls
 3sonarqube_conf  sonarqube_data  sonarqube_extensions  sonarqube_logs
 4
 5# cat sonarqube_conf/sonar.properties
 6sonar.web.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-1.8.1.jar=web
 7sonar.ce.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-1.8.1.jar=ce
 8
 9# ls sonarqube_extensions/plugins/
10sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar  sonar-l10n-zh-plugin-8.9.jar  sonarqube-community-branch-plugin-1.8.0.jar
  • 源文档描述:
11. Copy the plugin JAR file to the extensions/plugins/ directory of your SonarQube instance
2
32. Add -javaagent:./extensions/plugins/sonarqube-community-branch-plugin-${version}.jar=web to the sonar.web.javaAdditionalOptions property in your Sonarqube installation's config/sonar.properties file, e.g. sonar.web.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-1.8.0.jar=web
4
53. Add -javaagent:./extensions/plugins/sonarqube-community-branch-plugin-${version}.jar=ce to the sonar.ce.javaAdditionalOptions property in your Sonarqube installation's config/sonar.properties file, e.g. sonar.ce.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-1.8.0.jar=ce
6
74. Start Sonarqube, and accept the warning about using third-party plugins

image-20220528092936614

💘 实践:SonarQube多分支代码扫描(测试成功)-2023.6.25

image-20220528145709149

  • 实验环境
1jenkins/jenkins:2.346.3-2-lts-jdk11
2gitlab/gitlab-ce:15.0.3-ce.0
3sonarqube:9.9.0-community
4SonarScanner 4.8.0.2856
  • 实验软件(无)
  • 我们可以看到这里默认是没有其他分支的:

image-20230625073331766

  • 官网下载插件:

https://github.com/mc1arke/sonarqube-community-branch-plugin

本次找到1.14.0插件:

image-20230625073658499

https://github.com/mc1arke/sonarqube-community-branch-plugin/releases/download/1.14.0/sonarqube-community-branch-plugin-1.14.0.jar

image-20230625073618945

  • 配置:
 1#(1)下载插件
 2[root@Devops6 ~]#cd /data/devops6/sonarqube/
 3[root@Devops6 sonarqube]#ls
 4sonarqube_conf  sonarqube_data  sonarqube_extensions  sonarqube_logs
 5[root@Devops6 sonarqube]#cd sonarqube_extensions/plugins/
 6[root@Devops6 plugins]#ls
 7sonar-l10n-zh-plugin-9.9.jar
 8[root@Devops6 plugins]#wget https://gh.ddlc.top/https://github.com/mc1arke/sonarqube-community-branch-plugin/releases/download/1.14.0/sonarqube-community-branch-plugin-1.14.0.jar
 9[root@Devops6 plugins]#ll -h
10total 13M
11-rw-r--r-- 1 1000 1000 69K Jun 24 08:17 sonar-l10n-zh-plugin-9.9.jar
12-rw-r--r-- 1 root root 13M Dec 31 23:46 sonarqube-community-branch-plugin-1.14.0.jar
13[root@Devops6 plugins]#
14
15#(2)更新sonar服务端的配置文件
16[root@Devops6 sonarqube]#pwd
17/data/devops6/sonarqube  
18[root@Devops6 sonarqube]#ls
19sonarqube_conf  sonarqube_data  sonarqube_extensions  sonarqube_logs
20[root@Devops6 sonarqube]#cd sonarqube_conf/
21[root@Devops6 sonarqube_conf]#ls
22[root@Devops6 sonarqube_conf]#vim sonar.properties #这里新建次文件
23sonar.web.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-1.14.0.jar=web
24sonar.ce.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-1.14.0.jar=ce
25
26#(3)重启sonarqube
27[root@Devops6 ~]#docker restart sonarqube9

重启sonarqube完成后,就可以看到这里的项目出现了分支字样:

image-20230625074748550

  • 在devops-maven-service项目以master分支创建一个dev分支:

image-20230625074854064

  • 修改jenkins共享库里Sonar.groovy的代码:

image-20230625075339301

 1@Library("devops06@main") _
 2
 3//import src/org/devops/Build.groovy
 4def build = new org.devops.Build()
 5
 6pipeline {
 7    agent {label "build"}
 8    stages{
 9        stage("CheckOut"){
10            steps{
11                script{
12                    build.CheckOut()
13                }
14            }
15        }
16
17        stage("Build"){
18            steps{
19                script{
20                    build.Build()
21                }
22            }
23
24        }        
25
26        stage("CodeScan"){
27            steps{
28                script{
29
30                    // withCredentials([usernamePassword(credentialsId: '003d1667-653e-40f3-8103-b74614643aba', 
31                    //                 passwordVariable: 'SONAR_PASSWD', 
32                    //                 usernameVariable: 'SONAR_USER')]) {
33                                        
34                    //     sh """/data/devops6/sonar-scanner-4.8.0.2856-linux/bin/sonar-scanner \
35                    //         -Dsonar.login=${SONAR_USER} \
36                    //         -Dsonar.password=${SONAR_PASSWD} \
37                    //         -Dsonar.host.url=http://172.29.9.101:9000
38                    //     """
39                    // }
40
41
42                    withSonarQubeEnv(credentialsId: 'bdefe5e2-62a0-4e20-bf5a-51abedfe6df1') {
43                        sh """/data/devops6/sonar-scanner-4.8.0.2856-linux/bin/sonar-scanner \
44                        -Dsonar.login=${SONAR_AUTH_TOKEN} \
45                        -Dsonar.host.url=${SONAR_HOST_URL} \
46                        -Dsonar.branch.name=${env.branchName}
47                        """
48                    }
49
50                }
51            }
52        }
53
54    }
55}

提交。

  • 然后,然后在jenkins上跑一次流水线,本次是以dev分支跑:

image-20230625075253222

image-20220528112618449

image-20230625075316457

符合预期,测试结束。😘

5.扫描结果关联commitid

⚠️ 注意:

7.几,8.几的版本有这个插件,但9版本已经不支持次功能了。

提前装好插件:https://github.com/gabrie-allaigre/sonar-gitlab-plugin/tree/4.1.0-SNAPSHOT插件的说明文档查看该插件的Readme文档。 (仅质量阈失败后才可以展示扫描报告)

1# cp sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar  /data/cicd/sonarqube/sonarqube_extensions/plugins/
2
3# chmod +x /data/cicd/sonarqube/sonarqube_extensions/plugins/sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar
4
5# docker restart sonarqube

image-20220528152445659

1-Dsonar.gitlab.failure_notification_mode 值为commit-status表示更改提交状态, 值为nothing不做任何动作。
 1sonar-scanner -Dsonar.host.url=http://192.168.1.200:9000 \
 2-Dsonar.projectKey=devops-jacoco-service \
 3-Dsonar.projectName=devops-jacoco-service \
 4-Dsonar.projectVersion=1.0 \
 5-Dsonar.login=0809881d71f2b06b64786ae3f81a9acf22078e8b \
 6-Dsonar.ws.timeout=30 \
 7-Dsonar.projectDescription="my first project!" \
 8-Dsonar.links.homepage=http://www.baidu.com \
 9-Dsonar.sources=src \
10-Dsonar.sourceEncoding=UTF-8 \
11-Dsonar.java.binaries=target/classes \
12-Dsonar.java.test.binaries=target/test-classes \
13-Dsonar.java.surefire.report=target/surefire-reports \
14-Dsonar.core.codeCoveragePlugin=jacoco \
15-Dsonar.jacoco.reportPaths=coverage/jacoco.exec \
16-Dsonar.gitlab.commit_sha=f898a9fdbd319e68d519aa2ff42ad80da5186103 \
17-Dsonar.gitlab.ref_name=main \
18-Dsonar.gitlab.project_id=37 \
19-Dsonar.dynamicAnalysis=reuseReports \
20-Dsonar.gitlab.failure_notification_mode=commit-status \
21-Dsonar.gitlab.url=http://192.168.1.200 \
22-Dsonar.gitlab.user_token=CwmDA_4TKevDPRh4_SEf \
23-Dsonar.gitlab.api_version=v4

image-20220528152526270

1.静态配置

💘 实践:扫描结果关联commitid(静态配置)(测试failed)-2022.6.3

image-20220604093406610

自己实际测试过程:

  • 安装插件:

提前装好插件:https://github.com/gabrie-allaigre/sonar-gitlab-plugin/tree/4.1.0-SNAPSHOT

image-20220528153339071

1#(1)上传插件
2[root@devops ~]#ll -h sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar
3-rw-r--r-- 1 root root 9.9M May 28 15:43 sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar
4[root@devops ~]#docker cp sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar sonarqube:/opt/sonarqube/extensions/plugins
5
6#(2)重启sonarqube容器
7[root@devops ~]#docker restart sonarqube
8sonarqube
  • 在gitlab上生成token:

image-20220528154004111

image-20220528154114783

  • 来到devops4-maven-service项目下:

找一次commit信息:并拷贝commitid

5abc33244088898bdfb494ed0a3fc134ab0f5c80

image-20220528154447483

可以看到这里的评论是空的:

image-20220528154529245

  • 核对该项目的相关信息:

image-20220528162246486

编辑,提交后构建运行,观察效果:

image-20220528162639835

image-20220528163644963

但是自己是没有效果的:……

好奇怪:

image-20220528195546475

==老师这里是有效果的==:😥

image-20220528195636389

image-20220528195702007

好奇怪……😥

这里先放在这里,先进行动态配置,观察效果。

2.动态配置

💘 实践:扫描结果关联commitid(动态配置)(测试成功)-2022.6.3

image-20220604093420729

image-20220604093454668

  • 那么,接下来配置下动态的,看下效果:
1-Dsonar.gitlab.commit_sha=f898a9fdbd319e68d519aa2ff42ad80da5186103 \
2-Dsonar.gitlab.ref_name=main \
3-Dsonar.gitlab.project_id=37 \
4-Dsonar.dynamicAnalysis=reuseReports \
5-Dsonar.gitlab.failure_notification_mode=commit-status \
6-Dsonar.gitlab.url=http://192.168.1.200 \
7-Dsonar.gitlab.user_token=CwmDA_4TKevDPRh4_SEf \
8-Dsonar.gitlab.api_version=v4
  • 获取commitID:
1//获取CommitID
2def GetCommitID(){
3    ID = sh returnStdout: true, script:"git rev-parse HEAD"
4    return ID -"\n"
5}
6
7[root@devops devops4-maven-service]#git rev-parse HEAD
8b1087191a26bf36b7f77f31093a9e226a8846674
  • 获取projectID:

因为我们是有规范的,jenkins的作业name和gitlab的项目是保持一致的:

image-20220529082809298

image-20220529083616704

1curl --location --request GET 'http://172.29.9.101/api/v4/projects?search=devops4-maven-service' \
2--header 'PRIVATE-TOKEN: MhEV52bNpbUnnSfNg1nc' \
3--header 'Authorization: Basic YWRtaW46YWRtaW4xMjM='

如果获取一个不存在的项目的话,这里返回值为空。

image-20220529091336258

  • 这里直接在jenkins的pipeline的回放里临时测试一下效果:

完整代码:

 1pipeline {
 2    agent {
 3        label "build"
 4    }
 5
 6    stages {
 7        stage("Run") {
 8                steps{
 9                    script{
10                        commitID = GetCommitID()
11                        groupName = "${JOB_NAME}".split('-')[0]
12                        projectID = GetProjectID("${JOB_NAME}", groupName)
13
14                        println("commitID: ${commitID}")
15                        println("projectID: ${projectID}")
16                    }
17                }
18        }
19    }
20}
21
22//获取CommitID
23def GetCommitID(){
24    ID = sh returnStdout: true, script:"git rev-parse HEAD"
25    return ID -"\n"
26}
27
28//获取ProjectID
29// fork
30// namespace 
31// usera/devops-service-app
32// userb/devops-service-app 
33def GetProjectID(projectName, groupName){
34    response = sh  returnStdout: true, 
35        script: """ 
36            curl --location --request GET \
37            http://172.29.9.101/api/v4/projects?search=${projectName} \
38            --header 'PRIVATE-TOKEN: MhEV52bNpbUnnSfNg1nc' \
39            --header 'Authorization: Basic YWRtaW46YWRtaW4xMjM='
40        """
41    response = readJSON text: response
42    if (response != []){
43        for (p in response) {
44            if (p["namespace"]["name"] == groupName){
45                return response[0]["id"]
46            }
47        }
48    }
49}

运行后效果如下:

image-20220529183235881

符合预期,但是注意下,这个commitID好像不是最新一次的commitID……

  • 方法都写好了,我们就把它加到共享库里去:

共享库例的代码如下:

Jenkinsfile

 1@Library("mylib@main") _     //加载共享库
 2import org.devops.*						// 导入库
 3
 4def checkout = new Checkout()    //New实例化
 5def build = new Build()
 6def unittest = new UnitTest()
 7def sonar = new Sonar()
 8def gitcli = new GitLab()
 9
10//env.buildType = "${JOB_NAME}".split("-")[1]
11
12//流水线
13pipeline {
14    agent { label "build" }
15
16    options {
17        skipDefaultCheckout true
18    }
19
20    stages{
21        stage("Checkout"){
22            steps{
23                script {
24                    println("GetCode")
25                    checkout.GetCode("${env.srcUrl}", "${env.branchName}")
26                }
27            }
28        }
29
30        stage("Build"){
31            steps{
32                script{
33                    println("Build")
34                    //build.CodeBuild("${env.buildType}")
35                    sh "${env.buildShell}"
36                   
37                }
38            }
39        }
40
41        /*stage("UnitTest"){
42            steps{
43                script{
44                    unittest.CodeTest("${env.buildType}")
45                }
46            }
47        }*/
48
49        stage("CodeScan"){
50            steps{
51                script{
52                    profileName = "${JOB_NAME}".split("-")[0]
53                    sonar.Init("${JOB_NAME}", "java", profileName)
54
55
56                    //commit-status
57                    commitID = gitcli.GetCommitID()
58                    groupName =profileName
59                    projectID = gitcli.GetProjectID("${JOB_NAME}", groupName)
60                    sonar.CodeScan("${env.branchName}", commitID, projectID)
61                    }
62                    
63                }
64        }
65        
66    }
67}

image-20220603172100559

GitLab.groovy

image-20220604091428000

 1package org.devops
 2
 3//获取CommitID
 4def GetCommitID(){
 5    ID = sh returnStdout: true, script:"git rev-parse HEAD"
 6    return ID -"\n"
 7}
 8
 9//获取ProjectID
10// fork
11// namespace 
12// usera/devops-service-app
13// userb/devops-service-app 
14def GetProjectID(projectName, groupName){
15    response = sh  returnStdout: true, 
16        script: """ 
17            curl --location --request GET \
18            http://172.29.9.101/api/v4/projects?search=${projectName} \
19            --header 'PRIVATE-TOKEN: MhEV52bNpbUnnSfNg1nc' \
20            --header 'Authorization: Basic YWRtaW46YWRtaW4xMjM='
21        """
22    response = readJSON text: response
23    if (response != []){
24        for (p in response) {
25            if (p["namespace"]["name"] == groupName){
26                return response[0]["id"]
27            }
28        }
29    }
30}
  • 此时,在jebkins的devops4-maven-service项目运行流水线,观察效果:

可以看到,构建成功:

image-20220604091622213

在gitlab的devops4-maven-service项目的commit查看效果:

image-20220604091725749

符合预期,测试结束。😘

6.控制代码扫描步骤运行

1stage("SonarScan"){
2	when {
3		environment name: 'skipSonar', value: 'false'
4	}
5}

image-20220529075645233

image-20220529075703587

💘 实践:SonarQube控制代码扫描步骤运行(测试成功)-2023.6.25

自己测试过程:

  • 实验环境
1jenkins/jenkins:2.346.3-2-lts-jdk11
2gitlab/gitlab-ce:15.0.3-ce.0
3sonarqube:9.9.0-community
4SonarScanner 4.8.0.2856
  • 实验软件(无)

  • 修改jenkins共享库里的代码:

修改Jenkinsfile共享库里的下代码,添加when片段:

image-20230625125723186

  • 然后在jenkins的devops6-maven-servicepipeline里添加选项参数:

image-20230625125806038

  • 最后,构建,并观察效果:

不扫描代码:

image-20230625125836029

image-20230625125824376

image-20230625125932868

扫描代码:

image-20230625125844404

image-20230625125906993

可以看到,本次就跳过代码扫描了。

测试结束。😘

FAQ

sonarQube漏洞

sonarQube也很危险,之前暴露过很多漏洞,因为你的源代码也会在sonarQube里保留下来。

sonarQube版本

1SonarQube:
2之前6版本还支持Mysql,现在的7版本不支持Mysql了,也是Jdk11了;
3
4要升级的话:
51.jdk要升级到jdk11;
62.数据库:要做迁移的;
7
8多分支代码扫描是企业版本的一个特性,但开源版本有一个插件,可以实现这个功能。

snoar:代码扫描工具

sonar 代码质量检查:硬编码检查,安全检查,单元测试是否通过,代码质量

snoar:

  • 旧项目:还是算了哈哈。(一扫就扫除很多bug哈哈) 需要有一个宽容的规范,例如对于旧的项目,我们只需要考虑新增代码的质量,然后你再慢慢去改这个旧代码。

  • 新项目:用sonar去扫描,很容易改;一开始,就开发按照这个语言规范去写,基本上就没有什么问题;

SonQuar:5-9版本

企业里,6版本应该用的比较多些。

6版本是支持mysql的。

但7版本后,不支持mysql了。

image-20230624101529609

jdk版本需要注意:

从7开始,jdk就11以上了。

image-20230623105946080

Mysql–>迁移到 PG数据库。

image-20230623110031496

课程里使用docker安装的SonarQube。

image-20230623110121028

新版本已经集成了各语言规则插件

image-20230624112212379

SonarQube中各种语言的扫描规则都是以jar包的方式。在SonarQube8.9.1之前版本,默认没有安装语言规则插件, 需要手动安装。 服务端安装Java Code Quality and Security,SonarJS SonarGO 插件,并重启服务器。(如果这里由于网速原因下载不了插件,可以使用课程提供的压缩包,解压到downloads目录下然后重启sonarqube) 由于本次实验使用的是SonarQube8.9.1版本,所以不用在手动安装语言插件了。

1[root@devops ~]#docker exec -it sonarqube bash
2bash-5.1# cd /opt/sonarqube/lib/extensions/
3bash-5.1# ls
4sonar-csharp-plugin-8.22.0.31243.jar     sonar-jacoco-plugin-1.1.1.1157.jar       sonar-python-plugin-3.4.1.8066.jar
5sonar-css-plugin-1.4.2.2002.jar          sonar-java-plugin-6.15.1.26025.jar       sonar-ruby-plugin-1.8.3.2219.jar
6sonar-flex-plugin-2.6.1.2564.jar         sonar-javascript-plugin-7.4.4.15624.jar  sonar-scala-plugin-1.8.3.2219.jar
7sonar-go-plugin-1.8.3.2219.jar           sonar-kotlin-plugin-1.8.3.2219.jar       sonar-vbnet-plugin-8.22.0.31243.jar
8sonar-html-plugin-3.4.0.2754.jar         sonar-php-plugin-3.17.0.7439.jar         sonar-xml-plugin-2.2.0.2973.jar
9bash-5.1# 

如果低于该版本,可以参考如下操作。(可选,跳过)

 1[root@zeyang-nuc-service ~]# cd /data/cicd/plugin-sonar/
 2[root@zeyang-nuc-service plugin-sonar]# ls
 3sonar-go-plugin-1.6.0.719.jar            sonar-l10n-zh-plugin-1.29.jar
 4sonar-java-plugin-6.3.2.22818.jar        sonar-typescript-plugin-2.1.0.4359.jar
 5sonar-javascript-plugin-6.2.2.13315.jar
 6[root@zeyang-nuc-service plugin-sonar]# cp sonar-go-plugin-1.6.0.719.jar  sonar-java-plugin-6.3.2.22818.jar  sonar-javascript-plugin-6.2.2.13315.jar  sonar-typescript-plugin-2.1.0.4359.jar  /data/cicd/sonarqube/sonarqube_extensions/downloads/
 7[root@zeyang-nuc-service plugin-sonar]# ls /data/cicd/sonarqube/sonarqube_extensions/downloads/
 8sonar-go-plugin-1.6.0.719.jar      sonar-javascript-plugin-6.2.2.13315.jar
 9sonar-java-plugin-6.3.2.22818.jar  sonar-typescript-plugin-2.1.0.4359.jar
10[root@zeyang-nuc-service plugin-sonar]# chmod +x /data/cicd/sonarqube/sonarqube_extensions/downloads/*
11[root@zeyang-nuc-service plugin-sonar]# ls /data/cicd/sonarqube/sonarqube_extensions/downloads/
12sonar-go-plugin-1.6.0.719.jar      sonar-javascript-plugin-6.2.2.13315.jar
13sonar-java-plugin-6.3.2.22818.jar  sonar-typescript-plugin-2.1.0.4359.jar
14[root@zeyang-nuc-service plugin-sonar]# docker restart sonarqube
15sonarqube

GitLabCI-实践

🍀 老师之前写的一个python代码,可以拿来即用:

  • 编写一个python脚本:
  1import os 
  2import requests
  3import json
  4import sys
  5
  6
  7class SonarQube(object):
  8    def __init__(self, project_name, lang, profile_name, cmds):
  9        self.server_api = "http://192.168.1.200:9000/api/"
 10        self.auth_token = "YWRtaW46YWRtaW4xMjM="
 11        self.project_name = project_name
 12        self.lang = lang
 13        self.profile_name = profile_name
 14        self.cmds = cmds
 15
 16    def http_req(self, method, apiUrl):
 17        url = self.server_api + apiUrl
 18        payload={}
 19        headers = {
 20          'Authorization': 'Basic ' + self.auth_token
 21        }
 22        response = requests.request(method, url, headers=headers, data=payload)
 23        print(response.text)
 24
 25        if response.text != "":
 26            data = json.loads(response.text)
 27            return data
 28        return {}
 29
 30
 31    def SearchProject(self):
 32        """查找项目"""
 33        url = "projects/search?projects=" + self.project_name
 34        response = self.http_req("GET", url)
 35        if response["paging"]["total"] == 0:
 36            return False
 37        return True
 38
 39    def CreateProject(self):
 40        """创建项目"""
 41        apiUrl = "projects/create?name={0}&project={1}".format(self.project_name, self.project_name)
 42        response = self.http_req("POST", apiUrl)
 43        try:
 44            if response["project"]["key"] == self.project_name:
 45                return True 
 46        except Exception as e :
 47            print(e)
 48            print(response["errors"])
 49            return False
 50
 51    def UpdateQualityProfiles(self):
 52        apiUrl = "qualityprofiles/add_project?language={0}&project={1}&qualityProfile={2}".format(
 53            self.lang, self.project_name, self.profile_name)
 54
 55        response = self.http_req("POST", apiUrl)
 56
 57        try :
 58            print("ERROR: UpdateQualityProfiles{0}...".format(response["errors"]))
 59            return False
 60        except Exception as e :
 61            print(e)
 62            print("SUCCESS: UpdateQualityProfiles {0} > {1} > ${2}".format(
 63                self.lang, self.project_name, self.profile_name))
 64            return True
 65            
 66
 67    def SonarScan(self):
 68        result = os.system(self.cmds)
 69        if result == 0:
 70            return True
 71        return False
 72
 73
 74    def run(self):
 75        if not self.SearchProject():
 76            self.CreateProject()
 77        self.UpdateQualityProfiles()
 78        return self.SonarScan()
 79
 80if __name__ == '__main__':
 81    lang = sys.argv[1]
 82    profile_name = sys.argv[2]
 83    CI_PROJECT_NAME, CI_COMMIT_SHA, SONAR_AUTH_TOKEN,CI_PROJECT_TITLE,CI_PROJECT_URL,CI_PIPELINE_URL,CI_COMMIT_REF_NAME,CI_PROJECT_ID,CI_SERVER_URL ,GITLAB_ADMIN_TOKEN = sys.argv[3:]
 84    print(CI_PROJECT_NAME)
 85    sonarcmds = """
 86sonar-scanner \
 87-Dsonar.host.url=http://192.168.1.200:9000 \
 88-Dsonar.projectKey={0} \
 89-Dsonar.projectName={0} \
 90-Dsonar.projectVersion={1} \
 91-Dsonar.login={2} \
 92-Dsonar.ws.timeout=30 \
 93-Dsonar.projectDescription={3}  \
 94-Dsonar.links.homepage={4} \
 95-Dsonar.links.ci={5} \
 96-Dsonar.sources=src \
 97-Dsonar.sourceEncoding=UTF-8 \
 98-Dsonar.java.binaries=target/classes \
 99-Dsonar.java.test.binaries=target/test-classes \
100-Dsonar.java.surefire.report=target/surefire-reports \
101-Dsonar.core.codeCoveragePlugin=jacoco \
102-Dsonar.jacoco.reportPaths=target/jacoco.exec \
103-Dsonar.gitlab.commit_sha={1} \
104-Dsonar.gitlab.ref_name={6} \
105-Dsonar.gitlab.project_id={7} \
106-Dsonar.dynamicAnalysis=reuseReports \
107-Dsonar.gitlab.failure_notification_mode=nothing \
108-Dsonar.gitlab.url={8} \
109-Dsonar.gitlab.user_token={9} \
110-Dsonar.gitlab.api_version=v4
111""".format( 
112CI_PROJECT_NAME, 
113CI_COMMIT_SHA, 
114SONAR_AUTH_TOKEN,
115CI_PROJECT_TITLE,
116CI_PROJECT_URL,
117CI_PIPELINE_URL,
118CI_COMMIT_REF_NAME,
119CI_PROJECT_ID,
120CI_SERVER_URL,
121GITLAB_ADMIN_TOKEN)
122     
123    result = SonarQube(CI_PROJECT_NAME, lang, profile_name, sonarcmds ).run()
124    print(result)
  • 脚本调用:
1python3 sonarqube.py  \
2"java" "devops03" "devops-test" "99d098ef066b79d577a98220a17959465f4dd750"  "9e7e39a14a96bc886fdde43388b91e810491b7dc" "devops" "http://192.168.1.200/devops/devops-maven-service"  "http://192.168.1.200:8080/job/demo-pipeline-service/" "master" "5" "http://192.168.1.200" "apF1R9s9JJBYJzLF5mYd"
  • gitlabCI.yml
 1include:
 2  - project: 'devops03/devops03-gitlabci-lib'
 3    ref: main
 4    file: 
 5      - '/jobs/CI.yaml'
 6
 7workflow:
 8  rules:
 9    - if: $CI_PIPELINE_SOURCE == "web"
10      when: always
11    - if: $CI_COMMIT_BEFORE_SHA == "0000000000000000000000000000000000000000"
12      when: never
13    - when: always
14
15variables:
16  GIT_CHECKOUT: "false"   ## 全局关闭作业代码下载
17  BUILD_SHELL: "mvn clean package -DskipTests -s settings.xml"  ## 构建命令
18  TEST_SHELL: "mvn test -s settings.xml"                        ## 测试命令
19  ARTIFACT_PATH: "target/*.jar"                                 ## 制品路径
20  TEST_REPORTS: "target/surefire-reports/TEST-*.xml"            ## 测试报告
21
22stages:
23  - build
24  - test
25  - sonarscan
26
27pipelineInit:
28  extends: 
29    - .pipelineInit
30
31cibuild:
32  extends:
33    - .cibuild
34
35citest:
36  extends:
37    - .citest
38sonarscan:
39  tags:
40    - build 
41  stage: sonarscan
42  script:
43    |- 
44      curl "http://192.168.1.200/devops03/devops03-gitlabci-lib/-/raw/main/utils/SonarQube.py" \
45      -o sonarqube.py -s
46      python sonarqube.py  "java" ${CI_PROJECT_ROOT_NAMESPACE} ${CI_PROJECT_NAME} ${CI_COMMIT_SHA} \
47      ${SONAR_AUTH_TOKEN} ${CI_PROJECT_TITLE} ${CI_PROJECT_URL} ${CI_PIPELINE_URL}  ${CI_COMMIT_REF_NAME} \
48      ${CI_PROJECT_ID} ${CI_SERVER_URL}  ${GITLAB_ADMIN_TOKEN} 
49       

🍀 自己本次使用gitlabci测试

  • 先删除devops4-maven-service代码库的sonar-project.properties文件的一些代码:

image-20220604095621871

  • 修改gitlab模板库里的代码:

devops4-gitlablib-service

就是把jenkins里的代码拿过来就好:

image-20220604095953805

修改jobs/CI.yml

这里找一下gitlab的变量:

https://docs.gitlab.com/ee/ci/variables/predefined_variables.html

image-20220604100516381

image-20220604101041153

image-20220604101516395

  • 在devops4组里配置token:

MhEV52bNpbUnnSfNg1nc

image-20220604103751591

  • 最终配置如下:

image-20220604105338164

1      -Dsonar.branch.name=${CI_COMMIT_BRANCH} \
2      -Dsonar.gitlab.commit_sha=${CI_COMMIT_SHA} \
3      -Dsonar.gitlab.ref_name=${CI_COMMIT_BRANCH} \
4      -Dsonar.gitlab.project_id=${CI_PROJECT_ID} \
5      -Dsonar.dynamicAnalysis=reuseReports \
6      -Dsonar.gitlab.failure_notification_mode=commit-status \
7      -Dsonar.gitlab.url=http://172.29.9.101 \
8      -Dsonar.gitlab.user_token=${GITLAB_TOKEN} \
9      -Dsonar.gitlab.api_version=v4
  • 在gitlab的devops4-maven-service项目里提交一次流水线,观察效果:

image-20220604105530173

image-20220604105616024

image-20220604105509519

符合预期,测试结束。😘

SonarQube 插件

关于我

我的博客主旨:

  • 排版美观,语言精炼;
  • 文档即手册,步骤明细,拒绝埋坑,提供源码;
  • 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!

🍀 微信二维码 x2675263825 (舍得), qq:2675263825。

image-20230107215114763

🍀 微信公众号 《云原生架构师实战》

image-20230107215126971

🍀 语雀

https://www.yuque.com/xyy-onlyone

image-20230624093747671

🍀 csdn https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

image-20230107215149885

🍀 知乎 https://www.zhihu.com/people/foryouone

image-20230107215203185

最后

好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!

image-20230626072020036

推荐使用微信支付
微信支付二维码
推荐使用支付宝
支付宝二维码
最新文章

文档导航