代码质量平台实践-SonarQube

news2024/11/23 16:45:17

代码质量平台实践-SonarQube

image-20220524071501389

目录

image-20230626072432094

推荐文章

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配置文件的参数:

# 定义唯一的关键字
sonar.projectKey=devops6-maven-service

# 定义项目名称
sonar.projectName=devops6-maven-service

# 定义项目的版本信息
sonar.projectVersion=1.0
 
# 指定扫描代码的目录位置(多个逗号分隔)
sonar.sources=.
 
# 执行项目编码
sonar.sourceEncoding=UTF-8

# 指定sonar Server
sonar.host.url=http://172.29.9.101:9000

# 认证信息
sonar.login=admin
sonar.password=Admin@123

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

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

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

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

# 指定配置文件
sonar-scanner -Dproject.settings=myproject.properties

# 命令行传参
sonar-scanner -Dsonar.projectKey=myproject -Dsonar.sources=src1

1.Java项目扫描

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

image-20230624112316659

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

image-20230624112921445

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

  • 手动扫描代码

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

# 定义唯一的关键字
sonar.projectKey=devops6-maven-service

# 定义项目名称
sonar.projectName=devops6-maven-service

# 定义项目的版本信息
sonar.projectVersion=1.0
 
# 指定扫描代码的目录位置(多个逗号分隔)
sonar.sources=.
 
# 执行项目编码
sonar.sourceEncoding=UTF-8

# 指定sonar Server
sonar.host.url=http://172.29.9.101:9000

# 认证信息
sonar.login=admin
sonar.password=Admin@123

# java classes
sonar.java.binaries=target/classes 
sonar.java.test.binaries=target/test-classes 
sonar.java.surefire.report=target/surefire-reports

image-20230624115524654

提交。

  • 在本地devops6-maven-service本地项目里,拉取代码,然后扫描
#拉取代码
[root@Devops6 devops6-maven-service]#pwd
/data/devops6/devops6-maven-service
[root@Devops6 devops6-maven-service]#ls
mvnw  mvnw.cmd  pom.xml  src  target
[root@Devops6 devops6-maven-service]#git pull
[root@Devops6 devops6-maven-service]#ls
mvnw  mvnw.cmd  pom.xml  sonar-project.properties  src  target

#删除之前的打包好的缓存数据
[root@Devops6 devops6-maven-service]#rm -rf target/

#打包
[root@Devops6 devops6-maven-service]#mvn clean package

#扫描
[root@Devops6 devops6-maven-service]#sonar-scanner

image-20230624120241295

image-20230624120314686

  • 查看扫描报告

image-20230624120342846

测试结束。😘

2.Web前端项目扫描

image-20220526160925155

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

3.Golang项目扫描

image-20220526160958659

sonar-scanner -Dsonar.projectKey=devops-golang-service \
-Dsonar.projectName=devops-golang-service \
-Dsonar.sources=src \
-Dsonar.login=admin \
-Dsonar.password=admin \
-Dsonar.host.url=http://192.168.1.200:9000


## 有测试用例的情况
sonar.exclusions=**/*_test.go
sonar.tests=.
sonar.test.inclusions=**/*_test.go

4、CI流水线集成

1.JenkinsPipeline集成

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

(1)使用命令行方式

💘 实践:Jenkins集成SonarQube(命令行方式)(测试成功)-2023.6.24
  • 实验环境
jenkins/jenkins:2.346.3-2-lts-jdk11
gitlab/gitlab-ce:15.0.3-ce.0
sonarqube:9.9.0-community
SonarScanner 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

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

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

image-20230624212603600

# 定义唯一的关键字
sonar.projectKey=devops6-maven-service

# 定义项目名称
sonar.projectName=devops6-maven-service

# 定义项目的版本信息
sonar.projectVersion=1.0
 
# 指定扫描代码的目录位置(多个逗号分隔)
sonar.sources=.
 
# 执行项目编码
sonar.sourceEncoding=UTF-8

# 指定sonar Server
# sonar.host.url=http://172.29.9.101:9000

# 认证信息
# sonar.login=admin
# sonar.password=Admin@123

# java classes
sonar.java.binaries=target/classes 
sonar.java.test.binaries=target/test-classes 
sonar.java.surefire.report=target/surefire-reports
  • 这里编辑pipeline代码

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

image-20230624213624271

  • 继续改写Jenkins pipeline代码
@Library("devops06@main") _

//import src/org/devops/Build.groovy
def build = new org.devops.Build()

pipeline {
    agent {label "build"}
    stages{
        stage("CheckOut"){
            steps{
                script{
                    build.CheckOut()
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    build.Build()
                }
            }

        }        

        stage("CodeScan"){
            steps{
                script{

                    withCredentials([usernamePassword(credentialsId: '003d1667-653e-40f3-8103-b74614643aba', 
                                    passwordVariable: 'SONAR_PASSWD', 
                                    usernameVariable: 'SONAR_USER')]) {
                                        
                        sh """/data/devops6/sonar-scanner-4.8.0.2856-linux/bin/sonar-scanner \
                            -Dsonar.login=${SONAR_USER} \
                            -Dsonar.password=${SONAR_PASSWD} \
                            -Dsonar.host.url=http://172.29.9.101:9000
                        """
                    }


                }
            }
        }

    }
}

image-20230624215259682

提交。

  • 运行

image-20230624215316515

image-20230624215232679

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

image-20230624215409500

测试结束。😘

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

💘 实践:Jenkins集成SonarQube(Jenkins插件方式)(测试成功)-2023.6.24
  • 实验环境
jenkins/jenkins:2.346.3-2-lts-jdk11
gitlab/gitlab-ce:15.0.3-ce.0
sonarqube:9.9.0-community
SonarScanner 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
sqa_1339c18e86d442f9209bf363376554d5df5ca7a5

我的账号-生成令牌。

image-20230624215859394

  • 将token保存到Jenkins凭据中

image-20230624220323575

image-20230624220415524

image-20230624220426170

生成代码:

image-20230624222545276

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

image-20230624220042729

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

image-20230624222054199

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

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

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

@Library("devops06@main") _

//import src/org/devops/Build.groovy
def build = new org.devops.Build()

pipeline {
    agent {label "build"}
    stages{
        stage("CheckOut"){
            steps{
                script{
                    build.CheckOut()
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    build.Build()
                }
            }

        }        

        stage("CodeScan"){
            steps{
                script{

                    // withCredentials([usernamePassword(credentialsId: '003d1667-653e-40f3-8103-b74614643aba', 
                    //                 passwordVariable: 'SONAR_PASSWD', 
                    //                 usernameVariable: 'SONAR_USER')]) {
                                        
                    //     sh """/data/devops6/sonar-scanner-4.8.0.2856-linux/bin/sonar-scanner \
                    //         -Dsonar.login=${SONAR_USER} \
                    //         -Dsonar.password=${SONAR_PASSWD} \
                    //         -Dsonar.host.url=http://172.29.9.101:9000
                    //     """
                    // }


                    withSonarQubeEnv(credentialsId: 'bdefe5e2-62a0-4e20-bf5a-51abedfe6df1') {
                        sh """/data/devops6/sonar-scanner-4.8.0.2856-linux/bin/sonar-scanner \
                        -Dsonar.login=${SONAR_AUTH_TOKEN} \
                        -Dsonar.host.url=${SONAR_HOST_URL}
                        """
                    }

                }
            }
        }

    }
}

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

.pipelineInit:
  tags:
    - "${RUNNER_TAG}"
  stage: .pre
  variables:
    GIT_CHECKOUT: "true"   ##局部开启作业的代码下载
  script:
    - ls -l 

.cibuild:
  tags:
    - "${RUNNER_TAG}"
  stage: build
  script:
    - echo "${BUILD_SHELL}"
    - ${BUILD_SHELL}
  # artifacts:
  #    paths:
  #     - ${ARTIFACT_PATH}

.citest:
  tags:
    - "${RUNNER_TAG}"
  stage: test
  script:
    - echo "${TEST_SHELL}"
    - ${TEST_SHELL}
  # artifacts:
  #   reports:
  #     junit: ${TEST_REPORTS}

.codescan:
  tags: 
    - "${RUNNER_TAG}"
  stage: codescan
  script:
    |-
      /usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/bin/sonar-scanner \
      -Dsonar.login=${SONAR_USER} \
      -Dsonar.password=${SONAR_PASSWD} \
      -Dsonar.projectVersion=${CI_COMMIT_BRANCH}

编辑.gitlabci.yml文件:

image-20220526131503593

include:
  - project: 'devops4/devops4-gitlablib-service'
    ref: main
    file: 
      - '/jobs/CI.yaml'

workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "web"
      when: always
    - if: $CI_COMMIT_BEFORE_SHA == "0000000000000000000000000000000000000000"
      when: never
    - when: always

variables:
  GIT_CHECKOUT: "false"   ## 全局关闭作业代码下载
  BUILD_SHELL: "sh -x build.sh"  ## 构建命令
  TEST_SHELL: "/usr/local/apache-maven-3.8.5/bin/mvn test "                        ## 测试命令
  # ARTIFACT_PATH: "target/*jar"      ## 制品路径  
  # TEST_REPORTS: "target/surefire-reports/TEST-*.xml" ##测试报告
  RUNNER_TAG: "builder"

stages:
  - build
  - test
  - codescan

pipelineInit:
  extends: 
    - .pipelineInit

cibuild:
  extends:
    - .cibuild

citest:
  extends:
    - .citest

codescan:
  extends:
  - .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

withSonarQubeEnv(credentialsId: '09a996dd-2b05-46c8-90f9-09905bbc22c9') {
    // some block
}
  • 然后在之前的devops4-maven-service回放里编写下代码:
@Library("mylib@main") _     //加载共享库
import org.devops.*                     // 导入库

def checkout = new Checkout()    //New实例化
def build = new Build()
def unittest = new UnitTest()
def sonar = new Sonar()

//env.buildType = "${JOB_NAME}".split("-")[1]

//流水线
pipeline {
    agent { label "build" }

    options {
        skipDefaultCheckout true
    }

    stages{
        stage("Checkout"){
            steps{
                script {
                    println("GetCode")
                    checkout.GetCode("${env.srcUrl}", "${env.branchName}")
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    println("Build")
                    //build.CodeBuild("${env.buildType}")
                    sh "${env.buildShell}"
                   
                }
            }
        }

        /*stage("UnitTest"){
            steps{
                script{
                    unittest.CodeTest("${env.buildType}")
                }
            }
        }*/

        stage("CodeScan"){
            steps{
                script{
                   
                    withSonarQubeEnv(credentialsId: '09a996dd-2b05-46c8-90f9-09905bbc22c9') {
                        cliPath="/usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/bin"
                        sh """${cliPath}/sonar-scanner  \
                                -Dsonar.projectVersion=master
                        """
                    }
                }
            }
        
        }
    }
}
  • 运行并观察效果:

相比命令行只是增加了一些扩展链接, 对于经常操作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

//查找项目
api/projects/search?projects=${projectName}"

//创建项目
api/projects/create?name=${projectName}&project=${projectName}"
   
//更新语言规则集
api/qualityprofiles/add_project?language=${language}&qualityProfile=${qualityProfile}&project=${projectName}"

//项目授权
api/permissions/apply_template?projectKey=${projectKey}&templateName=${templateName}"

//更新质量阈
api/qualitygates/select?projectKey=${projectKey}&gateId=${gateId}"

image-20220527093552171

SonarQube API的请求方法

curl --location \
--request GET \
'http://192.168.1.200:9000/api/projects/search?projects=day4-maven2-service' \
--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进行解析和处理。【所以有了下面的代码】

def SonarRequest(apiUrl,method){
    withCredentials([string(credentialsId: "52df4ad9-7167-4bf6-a1fc-2f9f17713472", variable: 'SONAR_TOKEN')]) {
        sonarApi = "http://192.168.1.200:9000/api"
        response = sh  returnStdout: true, 
            script: """
            curl --location \
                 --request ${method} \
                 "${sonarApi}/${apiUrl}" \
                 --header "Authorization: Basic ${SONAR_TOKEN}"
            """
        try {
            response = readJSON text: """ ${response - "\n"} """
        } catch(e){
            response = readJSON text: """{"errors" : true}"""
        }
        return response
        
    }
}

1.查找项目

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

请求类型: GET

  • postman调试:

image-20220527100250862

image-20220527100324390

转换cURL命令:

curl --location --request GET 'http://172.29.9.101:9000/api/projects/search?projects=devops4-maven-service' \
--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项目的回放里进行测试:
pipeline {
	agent {
		label "build"
	}

	stages {
		stage("run"){
			steps{
				script{
					result = ProjectSearch("devops4-maven-service")
					println(result)
				}
			}
		}
	}
}

// 查找项目
def ProjectSearch(projectName){
    apiUrl = "projects/search?projects=${projectName}"
    response = SonarRequest(apiUrl,"GET")

    if (response.paging.total == 0){
        println("Project not found!.....")
        return false
    } 
    return true
}


def SonarRequest(apiUrl,method){
    withCredentials([string(credentialsId: "66b7dd41-5434-43a3-8d02-505e2f2dc38f", variable: 'SONAR_TOKEN')]) {
        sonarApi = "http://172.29.9.101:9000/api"
        response = sh  returnStdout: true, 
            script: """
            curl --location \
                 --request ${method} \
                 "${sonarApi}/${apiUrl}" \
                 --header "Authorization: Basic ${SONAR_TOKEN}"
            """
        try {
            response = readJSON text: """ ${response - "\n"} """
        } catch(e){
            response = readJSON text: """{"errors" : true}"""
        }
        return response
        
    }
}

运行测试:

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:

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

完整代码如下:

pipeline {
	agent {
		label "build"
	}

	stages {
		stage("run"){
			steps{
				script{
					projectName = "devops4-maven-service"
					result = ProjectSearch("devops4-maven-service")
					println(result)

					if (result == false){
						CreateProject(projectName)
					}
				}
			}
		}
	}
}

// 查找项目
def ProjectSearch(projectName){
    apiUrl = "projects/search?projects=${projectName}"
    response = SonarRequest(apiUrl,"GET")

    if (response.paging.total == 0){
        println("Project not found!.....")
        return false
    } 
    return true
}


def SonarRequest(apiUrl,method){
    withCredentials([string(credentialsId: "66b7dd41-5434-43a3-8d02-505e2f2dc38f", variable: 'SONAR_TOKEN')]) {
        sonarApi = "http://172.29.9.101:9000/api"
        response = sh  returnStdout: true, 
            script: """
            curl --location \
                 --request ${method} \
                 "${sonarApi}/${apiUrl}" \
                 --header "Authorization: Basic ${SONAR_TOKEN}"
            """
        try {
            response = readJSON text: """ ${response - "\n"} """
        } catch(e){
            response = readJSON text: """{"errors" : true}"""
        }
        return response
        
    }
}

// 创建项目
def CreateProject(projectName){
    apiUrl = "projects/create?name=${projectName}&project=${projectName}"
    response = SonarRequest(apiUrl,"POST")
    try{
        if (response.project.key == projectName ) {
            println("Project Create success!...")
            return true
        }
    }catch(e){
        println(response.errors)
        return false
    }
}

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:

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

Jenkins Pipeline:

// 更新项目质量配置
def UpdateQualityProfiles(lang, projectName, profileName){
    apiUrl = "qualityprofiles/add_project?language=${lang}&project=${projectName}&qualityProfile=${profileName}"
    response = SonarRequest(apiUrl,"POST")
    
    if (response.errors != true){
        println("ERROR: UpdateQualityProfiles ${response.errors}...")
        return false
    } else {
        println("SUCCESS: UpdateQualityProfiles ${lang} > ${projectName} > ${profileName}" )
        return true
    }
}

完整代码如下:

pipeline {
	agent {
		label "build"
	}

	stages {
		stage("run"){
			steps{
				script{
					projectName = "devops4-maven-service"
					result = ProjectSearch("devops4-maven-service")
					println(result)

					if (result == false){
						CreateProject(projectName)
					}

					UpdateQualityProfiles("java", projectName, "devops4")
				}
			}
		}
	}
}

// 查找项目
def ProjectSearch(projectName){
    apiUrl = "projects/search?projects=${projectName}"
    response = SonarRequest(apiUrl,"GET")

    if (response.paging.total == 0){
        println("Project not found!.....")
        return false
    } 
    return true
}


def SonarRequest(apiUrl,method){
    withCredentials([string(credentialsId: "66b7dd41-5434-43a3-8d02-505e2f2dc38f", variable: 'SONAR_TOKEN')]) {
        sonarApi = "http://172.29.9.101:9000/api"
        response = sh  returnStdout: true, 
            script: """
            curl --location \
                 --request ${method} \
                 "${sonarApi}/${apiUrl}" \
                 --header "Authorization: Basic ${SONAR_TOKEN}"
            """
        try {
            response = readJSON text: """ ${response - "\n"} """
        } catch(e){
            response = readJSON text: """{"errors" : true}"""
        }
        return response
        
    }
}

// 创建项目
def CreateProject(projectName){
    apiUrl = "projects/create?name=${projectName}&project=${projectName}"
    response = SonarRequest(apiUrl,"POST")
    try{
        if (response.project.key == projectName ) {
            println("Project Create success!...")
            return true
        }
    }catch(e){
        println(response.errors)
        return false
    }
}

// 更新项目质量配置
def UpdateQualityProfiles(lang, projectName, profileName){
    apiUrl = "qualityprofiles/add_project?language=${lang}&project=${projectName}&qualityProfile=${profileName}"
    response = SonarRequest(apiUrl,"POST")
    
    if (response.errors != true){
        println("ERROR: UpdateQualityProfiles ${response.errors}...")
        return false
    } else {
        println("SUCCESS: UpdateQualityProfiles ${lang} > ${projectName} > ${profileName}" )
        return true
    }
}

运行后效果:

image-20220527144057623

image-20220527144159867

4.控制逻辑(实践)

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

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

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

image-20220527150812463

image-20220527150829647

image-20220527150850351

image-20220527150911189

自动化实现方式 :

  • 查询项目是否存在

    • 存在: 直接更新质量配置
    • 不存在: 创建空的项目然后更新质量配置
stage("sonartest"){
            steps{
                script{

                    // 判断项目是否存在
                    sonarProjectName = "${JOB_NAME.split('/')[-1]}"
                    result = ProjectSearch(sonarProjectName)
                    println(result)

                    if (result != true){
                        println("Create Sonar Project!...")
                        CreateProject(sonarProjectName)
                    }

                    // 指定项目的配置
                    UpdateQualityProfiles("java", sonarProjectName, "${sonarProjectName.split('-')[0]}")
                }
            }
        }

完整的jenkinsfile代码:

pipeline {

    agent {
        label "build"
    }

    stages{
        stage("sonartest"){
            steps{
                script{

                    // 判断项目是否存在
                    sonarProjectName = "${JOB_NAME.split('/')[-1]}"
                    result = ProjectSearch(sonarProjectName)
                    println(result)

                    if (result != true){
                        println("Create Sonar Project!...")
                        CreateProject(sonarProjectName)
                    }

                    // 指定项目的配置
                    UpdateQualityProfiles("java", sonarProjectName, "${sonarProjectName.split('-')[0]}")
                }
            }
        }
    }
}

def SonarRequest(apiUrl,method){
    withCredentials([string(credentialsId: "52df4ad9-7167-4bf6-a1fc-2f9f17713472", variable: 'SONAR_TOKEN')]) {
        sonarApi = "http://192.168.1.200:9000/api"
        response = sh  returnStdout: true, 
            script: """
            curl --location \
                 --request ${method} \
                 "${sonarApi}/${apiUrl}" \
                 --header "Authorization: Basic ${SONAR_TOKEN}"
            """
        try {
            response = readJSON text: """ ${response - "\n"} """
        } catch(e){
            response = readJSON text: """{"errors" : true}"""
        }
        return response
        
    }
}

// 更新质量阈
def UpdateQualityProfiles(lang, projectName, profileName){
    apiUrl = "qualityprofiles/add_project?language=${lang}&project=${projectName}&qualityProfile=${profileName}"
    response = SonarRequest(apiUrl,"POST")
    
    if (response.errors != true){
        println("ERROR: UpdateQualityProfiles ${response.errors}...")
        return false
    } else {
        println("SUCCESS: UpdateQualityProfiles ${lang} > ${projectName} > ${profileName}" )
        return true
    }
}

// 创建项目
def CreateProject(projectName){
    apiUrl = "projects/create?name=${projectName}&project=${projectName}"
    response = SonarRequest(apiUrl,"POST")
    try{
        if (response.project.key == projectName ) {
            println("Project Create success!...")
            return true
        }
    }catch(e){
        println(response.errors)
        return false
    }
}

// 查找项目
def ProjectSearch(projectName){
    apiUrl = "projects/search?projects=${projectName}"
    response = SonarRequest(apiUrl,"GET")

    if (response.paging.total == 0){
        println("Project not found!.....")
        return false
    } 
    return true
}

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

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

image-20220527200631422

package org.devops

def CodeScan(branchName){
    cliPath="/usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/bin"
    withCredentials([usernamePassword(credentialsId: '79d8f75b-3733-49f4-950f-19f8480fda03', 
                                                passwordVariable: 'SONAR_PASSWD', 
                                                usernameVariable: 'SONAR_USER')]) {
        // some block
        sh """${cliPath}/sonar-scanner  \
            -Dsonar.login=${SONAR_USER} \
            -Dsonar.password=${SONAR_PASSWD} \
            -Dsonar.projectVersion=${branchName}
        """
    }
}

def Init(projectName, lang, profileName){
    result = ProjectSearch(projectName)
    println(result)

    if (result == false){
        CreateProject(projectName)
    }

    UpdateQualityProfiles(lang, projectName, profileName)
}



// 查找项目
def ProjectSearch(projectName){
    apiUrl = "projects/search?projects=${projectName}"
    response = SonarRequest(apiUrl,"GET")

    if (response.paging.total == 0){
        println("Project not found!.....")
        return false
    } 
    return true
}


def SonarRequest(apiUrl,method){
    withCredentials([string(credentialsId: "66b7dd41-5434-43a3-8d02-505e2f2dc38f", variable: 'SONAR_TOKEN')]) {
        sonarApi = "http://172.29.9.101:9000/api"
        response = sh  returnStdout: true, 
            script: """
            curl --location \
                 --request ${method} \
                 "${sonarApi}/${apiUrl}" \
                 --header "Authorization: Basic ${SONAR_TOKEN}"
            """
        try {
            response = readJSON text: """ ${response - "\n"} """
        } catch(e){
            response = readJSON text: """{"errors" : true}"""
        }
        return response
        
    }
}

// 创建项目
def CreateProject(projectName){
    apiUrl = "projects/create?name=${projectName}&project=${projectName}"
    response = SonarRequest(apiUrl,"POST")
    try{
        if (response.project.key == projectName ) {
            println("Project Create success!...")
            return true
        }
    }catch(e){
        println(response.errors)
        return false
    }
}

// 更新项目质量配置
def UpdateQualityProfiles(lang, projectName, profileName){
    apiUrl = "qualityprofiles/add_project?language=${lang}&project=${projectName}&qualityProfile=${profileName}"
    response = SonarRequest(apiUrl,"POST")
    
    if (response.errors != true){
        println("ERROR: UpdateQualityProfiles ${response.errors}...")
        return false
    } else {
        println("SUCCESS: UpdateQualityProfiles ${lang} > ${projectName} > ${profileName}" )
        return true
    }
}

jenkinsfile代码:

image-20220527161544699

@Library("mylib@main") _     //加载共享库
import org.devops.*						// 导入库

def checkout = new Checkout()    //New实例化
def build = new Build()
def unittest = new UnitTest()
def sonar = new Sonar()

//env.buildType = "${JOB_NAME}".split("-")[1]

//流水线
pipeline {
    agent { label "build" }

    options {
        skipDefaultCheckout true
    }

    stages{
        stage("Checkout"){
            steps{
                script {
                    println("GetCode")
                    checkout.GetCode("${env.srcUrl}", "${env.branchName}")
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    println("Build")
                    //build.CodeBuild("${env.buildType}")
                    sh "${env.buildShell}"
                   
                }
            }
        }

        /*stage("UnitTest"){
            steps{
                script{
                    unittest.CodeTest("${env.buildType}")
                }
            }
        }*/

        stage("CodeScan"){
            steps{
                script{
                    profileName = "${JOB_NAME}".split("-")[0]
                    sonar.Init("${JOB_NAME}", "java", profileName)
                    sonar.CodeScan("${env.branchName}")
                    }
                    
                }
        }
        
    }
}

注意:也要保证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

自己测试过程:

  • 测试环境
sonarqube: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

自己测试过程:

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

image-20230625062234005

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

image-20230625062329756

  • 也可以添加新的条件

image-20230625062459190

3.代码覆盖率统计

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

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

添加jacoco-maven-pluginjunit插件。

<dependencies>
    <dependency>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.8.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

添加插件:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-compiler-plugin</artifactId>
	<version>3.6.1</version>
	<configuration>
    	<skipMain>true</skipMain>
		<skip>true</skip>
		<source>1.8</source>
		<target>1.8</target>
	</configuration>
</plugin>


<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>0.7.5.201505241946</version>
  <executions>
    <execution>
      <id>prepare-agent</id>
      <goals>
        <goal>prepare-agent</goal>
      </goals>
    </execution>
    <execution>
      <id>report</id>
      <phase>prepare-package</phase>
      <goals>
        <goal>report</goal>
      </goals>
    </execution>
    <execution>
      <id>post-unit-test</id>
      <phase>test</phase>
      <goals>
        <goal>report</goal>
      </goals>
      <configuration>
        <dataFile>target/jacoco.exec</dataFile>
        <outputDirectory>target/jacoco-reports</outputDirectory>
      </configuration>
    </execution>
  </executions>
  <configuration>
    <systemPropertyVariables>
      <jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
    </systemPropertyVariables>
  </configuration>
</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

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

image-20220526195904254

image-20220526200141381

image-20220526200304214

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

测试时间:2023年6月25日

测试环境:

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

当前maven版本无jacoco插件:

image-20230625070317135

构建失败:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GdQoEgQV-1687735995069)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/image-20230625070530207.png)]

image-20230625070632543

4.多分支代码扫描

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

image-20220528092619465

1. Sonar 7.9.6版本

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

## 临时方案
docker exec -it sonarqube bash
cd /opt/sonarqube/lib/common
cp ../../extensions/plugins/sonarqube-community-branch-plugin-1.3.2 ./
exit

docker restart sonarqube 


## 持久化lib目录后
[root@zeyang-nuc-service sonarqube]# ls
sonarqube_conf  sonarqube_data  sonarqube_extensions  sonarqube_lib  sonarqube_logs
[root@zeyang-nuc-service sonarqube]# cp /root/sonarqube-community-branch-plugin-1.3.2.jar  sonarqube_extensions/plugins/
[root@zeyang-nuc-service sonarqube]# chmod +x sonarqube_extensions/plugins/sonarqube-community-branch-plugin-1.3.2.jar
[root@zeyang-nuc-service sonarqube]#
[root@zeyang-nuc-service sonarqube]#
[root@zeyang-nuc-service sonarqube]# cp /root/sonarqube-community-branch-plugin-1.3.2.jar  sonarqube_lib/common/
[root@zeyang-nuc-service sonarqube]# chmod +x sonarqube_lib/common/sonarqube-community-branch-plugin-1.3.2.jar
[root@zeyang-nuc-service sonarqube]#


docker restart sonarqube

image-20220528092757954

##扫描参数增加 –Dsonar.branch.name=

sonar-scanner -Dsonar.host.url=http://192.168.1.200:9000 \
-Dsonar.projectKey=devops-maven2-service \
-Dsonar.projectName=devops-maven2-service \
-Dsonar.projectVersion=1.0 \
-Dsonar.login=admin \
-Dsonar.password=admin \
-Dsonar.ws.timeout=30 \
-Dsonar.projectDescription="my first project!" \
-Dsonar.links.homepage=http://192.168.1.200/devops/devops-maven-service \
-Dsonar.links.ci=http://192.168.1.200:8080/job/demo-pipeline-service/ \
-Dsonar.sources=src \
-Dsonar.sourceEncoding=UTF-8 \
-Dsonar.java.binaries=target/classes \
-Dsonar.java.test.binaries=target/test-classes \
-Dsonar.java.surefire.report=target/surefire-reports \
-Dsonar.branch.name=release-1.1.1

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

ERROR: Error during SonarScanner execution
ERROR: No branches currently exist in this project. Please scan the main branch without passing any branch parameters.
ERROR:
ERROR: 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 。
# cd /data/cicd2/sonarqube/
# ls
sonarqube_conf  sonarqube_data  sonarqube_extensions  sonarqube_logs

# cat sonarqube_conf/sonar.properties
sonar.web.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-1.8.1.jar=web
sonar.ce.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-1.8.1.jar=ce

# ls sonarqube_extensions/plugins/
sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar  sonar-l10n-zh-plugin-8.9.jar  sonarqube-community-branch-plugin-1.8.0.jar
  • 源文档描述:
1. Copy the plugin JAR file to the extensions/plugins/ directory of your SonarQube instance

2. 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

3. 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

4. Start Sonarqube, and accept the warning about using third-party plugins

image-20220528092936614

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

image-20220528145709149

  • 实验环境
jenkins/jenkins:2.346.3-2-lts-jdk11
gitlab/gitlab-ce:15.0.3-ce.0
sonarqube:9.9.0-community
SonarScanner 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)下载插件
[root@Devops6 ~]#cd /data/devops6/sonarqube/
[root@Devops6 sonarqube]#ls
sonarqube_conf  sonarqube_data  sonarqube_extensions  sonarqube_logs
[root@Devops6 sonarqube]#cd sonarqube_extensions/plugins/
[root@Devops6 plugins]#ls
sonar-l10n-zh-plugin-9.9.jar
[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
[root@Devops6 plugins]#ll -h
total 13M
-rw-r--r-- 1 1000 1000 69K Jun 24 08:17 sonar-l10n-zh-plugin-9.9.jar
-rw-r--r-- 1 root root 13M Dec 31 23:46 sonarqube-community-branch-plugin-1.14.0.jar
[root@Devops6 plugins]#

#(2)更新sonar服务端的配置文件
[root@Devops6 sonarqube]#pwd
/data/devops6/sonarqube  
[root@Devops6 sonarqube]#ls
sonarqube_conf  sonarqube_data  sonarqube_extensions  sonarqube_logs
[root@Devops6 sonarqube]#cd sonarqube_conf/
[root@Devops6 sonarqube_conf]#ls
[root@Devops6 sonarqube_conf]#vim sonar.properties #这里新建次文件
sonar.web.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-1.14.0.jar=web
sonar.ce.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-1.14.0.jar=ce

#(3)重启sonarqube
[root@Devops6 ~]#docker restart sonarqube9

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

image-20230625074748550

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

image-20230625074854064

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

image-20230625075339301

@Library("devops06@main") _

//import src/org/devops/Build.groovy
def build = new org.devops.Build()

pipeline {
    agent {label "build"}
    stages{
        stage("CheckOut"){
            steps{
                script{
                    build.CheckOut()
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    build.Build()
                }
            }

        }        

        stage("CodeScan"){
            steps{
                script{

                    // withCredentials([usernamePassword(credentialsId: '003d1667-653e-40f3-8103-b74614643aba', 
                    //                 passwordVariable: 'SONAR_PASSWD', 
                    //                 usernameVariable: 'SONAR_USER')]) {
                                        
                    //     sh """/data/devops6/sonar-scanner-4.8.0.2856-linux/bin/sonar-scanner \
                    //         -Dsonar.login=${SONAR_USER} \
                    //         -Dsonar.password=${SONAR_PASSWD} \
                    //         -Dsonar.host.url=http://172.29.9.101:9000
                    //     """
                    // }


                    withSonarQubeEnv(credentialsId: 'bdefe5e2-62a0-4e20-bf5a-51abedfe6df1') {
                        sh """/data/devops6/sonar-scanner-4.8.0.2856-linux/bin/sonar-scanner \
                        -Dsonar.login=${SONAR_AUTH_TOKEN} \
                        -Dsonar.host.url=${SONAR_HOST_URL} \
                        -Dsonar.branch.name=${env.branchName}
                        """
                    }

                }
            }
        }

    }
}

提交。

  • 然后,然后在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文档。 (仅质量阈失败后才可以展示扫描报告)

# cp sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar  /data/cicd/sonarqube/sonarqube_extensions/plugins/

# chmod +x /data/cicd/sonarqube/sonarqube_extensions/plugins/sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar

# docker restart sonarqube

image-20220528152445659

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

image-20220528152526270

1.静态配置

2.动态配置

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

image-20220604093420729

image-20220604093454668

  • 那么,接下来配置下动态的,看下效果:
-Dsonar.gitlab.commit_sha=f898a9fdbd319e68d519aa2ff42ad80da5186103 \
-Dsonar.gitlab.ref_name=main \
-Dsonar.gitlab.project_id=37 \
-Dsonar.dynamicAnalysis=reuseReports \
-Dsonar.gitlab.failure_notification_mode=commit-status \
-Dsonar.gitlab.url=http://192.168.1.200 \
-Dsonar.gitlab.user_token=CwmDA_4TKevDPRh4_SEf \
-Dsonar.gitlab.api_version=v4
  • 获取commitID:
//获取CommitID
def GetCommitID(){
    ID = sh returnStdout: true, script:"git rev-parse HEAD"
    return ID -"\n"
}

[root@devops devops4-maven-service]#git rev-parse HEAD
b1087191a26bf36b7f77f31093a9e226a8846674
  • 获取projectID:

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

image-20220529082809298

image-20220529083616704

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

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

image-20220529091336258

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

完整代码:

pipeline {
    agent {
        label "build"
    }

    stages {
        stage("Run") {
                steps{
                    script{
                        commitID = GetCommitID()
                        groupName = "${JOB_NAME}".split('-')[0]
                        projectID = GetProjectID("${JOB_NAME}", groupName)

                        println("commitID: ${commitID}")
                        println("projectID: ${projectID}")
                    }
                }
        }
    }
}

//获取CommitID
def GetCommitID(){
    ID = sh returnStdout: true, script:"git rev-parse HEAD"
    return ID -"\n"
}

//获取ProjectID
// fork
// namespace 
// usera/devops-service-app
// userb/devops-service-app 
def GetProjectID(projectName, groupName){
    response = sh  returnStdout: true, 
        script: """ 
            curl --location --request GET \
            http://172.29.9.101/api/v4/projects?search=${projectName} \
            --header 'PRIVATE-TOKEN: MhEV52bNpbUnnSfNg1nc' \
            --header 'Authorization: Basic YWRtaW46YWRtaW4xMjM='
        """
    response = readJSON text: response
    if (response != []){
        for (p in response) {
            if (p["namespace"]["name"] == groupName){
                return response[0]["id"]
            }
        }
    }
}

运行后效果如下:

image-20220529183235881

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

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

共享库例的代码如下:

Jenkinsfile

@Library("mylib@main") _     //加载共享库
import org.devops.*						// 导入库

def checkout = new Checkout()    //New实例化
def build = new Build()
def unittest = new UnitTest()
def sonar = new Sonar()
def gitcli = new GitLab()

//env.buildType = "${JOB_NAME}".split("-")[1]

//流水线
pipeline {
    agent { label "build" }

    options {
        skipDefaultCheckout true
    }

    stages{
        stage("Checkout"){
            steps{
                script {
                    println("GetCode")
                    checkout.GetCode("${env.srcUrl}", "${env.branchName}")
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    println("Build")
                    //build.CodeBuild("${env.buildType}")
                    sh "${env.buildShell}"
                   
                }
            }
        }

        /*stage("UnitTest"){
            steps{
                script{
                    unittest.CodeTest("${env.buildType}")
                }
            }
        }*/

        stage("CodeScan"){
            steps{
                script{
                    profileName = "${JOB_NAME}".split("-")[0]
                    sonar.Init("${JOB_NAME}", "java", profileName)


                    //commit-status
                    commitID = gitcli.GetCommitID()
                    groupName =profileName
                    projectID = gitcli.GetProjectID("${JOB_NAME}", groupName)
                    sonar.CodeScan("${env.branchName}", commitID, projectID)
                    }
                    
                }
        }
        
    }
}

image-20220603172100559

GitLab.groovy

image-20220604091428000

package org.devops

//获取CommitID
def GetCommitID(){
    ID = sh returnStdout: true, script:"git rev-parse HEAD"
    return ID -"\n"
}

//获取ProjectID
// fork
// namespace 
// usera/devops-service-app
// userb/devops-service-app 
def GetProjectID(projectName, groupName){
    response = sh  returnStdout: true, 
        script: """ 
            curl --location --request GET \
            http://172.29.9.101/api/v4/projects?search=${projectName} \
            --header 'PRIVATE-TOKEN: MhEV52bNpbUnnSfNg1nc' \
            --header 'Authorization: Basic YWRtaW46YWRtaW4xMjM='
        """
    response = readJSON text: response
    if (response != []){
        for (p in response) {
            if (p["namespace"]["name"] == groupName){
                return response[0]["id"]
            }
        }
    }
}

  • 此时,在jebkins的devops4-maven-service项目运行流水线,观察效果:

可以看到,构建成功:

image-20220604091622213

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

image-20220604091725749

符合预期,测试结束。😘

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

stage("SonarScan"){
	when {
		environment name: 'skipSonar', value: 'false'
	}
}

image-20220529075645233

image-20220529075703587

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

自己测试过程:

  • 实验环境
jenkins/jenkins:2.346.3-2-lts-jdk11
gitlab/gitlab-ce:15.0.3-ce.0
sonarqube:9.9.0-community
SonarScanner 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

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

测试结束。😘

关于我

我的博客主旨:

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

🍀 微信二维码
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-20230626072033213
// userb/devops-service-app
def GetProjectID(projectName, groupName){
response = sh returnStdout: true,
script: “”"
curl --location --request GET
http://172.29.9.101/api/v4/projects?search=${projectName}
–header ‘PRIVATE-TOKEN: MhEV52bNpbUnnSfNg1nc’
–header ‘Authorization: Basic YWRtaW46YWRtaW4xMjM=’
“”"
response = readJSON text: response
if (response != []){
for (p in response) {
if (p[“namespace”][“name”] == groupName){
return response[0][“id”]
}
}
}
}




- 此时,在jebkins的devops4-maven-service项目运行流水线,观察效果:

可以看到,构建成功:

[外链图片转存中...(img-gSibJ7o7-1687735995076)]

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

[外链图片转存中...(img-I39K9EPM-1687735995076)]

符合预期,测试结束。😘

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

```groovy
stage("SonarScan"){
	when {
		environment name: 'skipSonar', value: 'false'
	}
}

[外链图片转存中…(img-T6X07g3f-1687735995077)]

[外链图片转存中…(img-YyI8IQEE-1687735995077)]

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

自己测试过程:

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

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

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

[外链图片转存中…(img-Tn6bNV3F-1687735995077)]

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

[外链图片转存中…(img-BZ1CDgZv-1687735995077)]

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

不扫描代码:

[外链图片转存中…(img-Avn69JMx-1687735995077)]

[外链图片转存中…(img-gZ6Xiv2P-1687735995078)]

[外链图片转存中…(img-TMfrBEfz-1687735995078)]

扫描代码:

[外链图片转存中…(img-cDURsZpy-1687735995078)]

[外链图片转存中…(img-nP0uW8RM-1687735995078)]

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

测试结束。😘

关于我

我的博客主旨:

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

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

[外链图片转存中…(img-XRvwGXq9-1687735995078)]

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

[外链图片转存中…(img-J3qSw6m1-1687735995078)]

🍀 语雀

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

[外链图片转存中…(img-N5Iozulj-1687735995079)]

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

[外链图片转存中…(img-VGMmxbjO-1687735995079)]

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

[外链图片转存中…(img-lxI6plPY-1687735995079)]

最后

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

[外链图片转存中…(img-VnaqUhrR-1687735995079)]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/685581.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java8新特性之--函数式接口加方法引用与构造器引用详细讲解

函数式(Functional)接口 什么是函数式(Functional)接口 只包含一个抽象方法的接口&#xff0c;称为函数式接口。 你可以通过 Lambda 表达式来创建该接口的对象。&#xff08;若 Lambda 表达式 抛出一个受检异常(即&#xff1a;非运行时异常)&#xff0c;那么该异常需要在目标…

Java集合容器面试题

集合 什么是集合 集合就是用于存储数据的容器&#xff0c;只能存储引用类型&#xff0c;所以集合非常适合用来存储对象。而且集合是长度可变&#xff0c;所以对象个数不确定的时候适合使用集合 集合的特点 1、集合只能存储引用数据类型。集合用于存储对象。 2、对象的个数…

centos版本的EDA虚拟机搭建3

0、参考博客 1、CentOS 7 下 rar unrar的安装 1、配置虚拟机与主机共享文件夹。 **前提&#xff0c;虚拟机关机。**之后进行虚拟机设置&#xff0c;选项-共享文件夹&#xff0c;点击总是启用&#xff0c;之后添加共享文件夹名称。 之后打开虚拟机&#xff0c;命令行输入v…

【Leetcode60天带刷】day34贪心—— ​860. 柠檬水找零​,406.根据身高重建队列

​ 题目&#xff1a; 860. 柠檬水找零 在柠檬水摊上&#xff0c;每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品&#xff0c;&#xff08;按账单 bills 支付的顺序&#xff09;一次购买一杯。 每位顾客只买一杯柠檬水&#xff0c;然后向你付 5 美元、10 美元或 20 美元。…

Python零基础入门(五)——超详细的列表和元组介绍

系列文章目录 个人简介&#xff1a;机电专业在读研究生&#xff0c;CSDN内容合伙人&#xff0c;博主个人首页 Python入门专栏&#xff1a;《Python入门》欢迎阅读&#xff0c;一起进步&#xff01;&#x1f31f;&#x1f31f;&#x1f31f; 码字不易&#xff0c;如果觉得文章不…

Mysql 学习总结(88)—— Mysql 官方为什么不推荐用雪花 id 和 uuid 做 MySQL 主键

问题 在 MySQL 中设计表的时候,MySQL 官方推荐不要使用 uuid 或者不连续不重复的雪花 id(long 型且唯一),而是推荐连续自增的主键 id,官方的推荐是 auto_increment,那么为什么不建议采用 uuid,使用 uuid 究竟有什么坏处? MySQL 和程序实例 1、要说明这个问题,我们首先…

开关电源-6中常用的防反接电路

6种常用防反接电路介绍 电源反接&#xff0c;会给电路造成损坏&#xff0c;电源反接是不可避免的。所以我们就需要给电路中加入保护电路&#xff0c;达到即使接反电源&#xff0c;也不会损坏的目的&#xff0c;下面介绍几种常用防反接电路。 一、单二极管防反接电路 二极管防…

RabbitMQ 2023面试5题(一)

一、RabbitMQ延时队列可以用于哪些场景 RabbitMQ延时队列可以用于以下场景&#xff1a; 订单处理&#xff1a;在电商网站中&#xff0c;订单处理是一个常见的业务流程。如果订单需要立即处理&#xff0c;可以使用RabbitMQ的延时队列来实现延迟处理。例如&#xff0c;可以将订单…

SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接

文章目录 前后端对接前端接口修改对接后端后端总体配置后端编写登录登出业务代码 测试后端所有代码 前后端对接 前端接口修改对接后端 src\api\user.js中修改请求地址&#xff0c;与后端保持一致 记录下前端的src\utils\request.js中的X-Token字段 改变开发环境中的请求地…

Golang每日一练(leetDay0108) 灯泡开关I\II Bulb Switcher

目录 319. 灯泡开关 Bulb Switcher &#x1f31f;&#x1f31f; 672. 灯泡开关II Bulb Switcher ii &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每…

基于Java校园快递一站式服务系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

【八大排序(十)】八大排序效率与稳定性分析

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:八大排序专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习排序知识   &#x1f51d;&#x1f51d; 八大排序总结 1. 前言2. 什么是排序算法的…

基于多模态变分对抗主动学习的下游医学图像分析任务

文章目录 M-VAAL: Multimodal Variational Adversarial Active Learning for Downstream Medical Image Analysis Tasks摘要本文方法实验结果 M-VAAL: Multimodal Variational Adversarial Active Learning for Downstream Medical Image Analysis Tasks 摘要 在医学领域&…

chatgpt赋能python:Python自动执行某个软件

Python自动执行某个软件 Python是一种简单易用且非常流行的编程语言&#xff0c;常用于自动化和数据分析。如果你想自动执行某个软件&#xff0c;那么Python将是一个非常好的选择。在本文中&#xff0c;我们将介绍如何使用Python自动执行某个软件&#xff0c;以及如何做好SEO优…

第 351 场LeetCode周赛

A 美丽下标对的数目 模拟 class Solution { public:int countBeautifulPairs(vector<int> &nums) {int n nums.size();int res 0;for (int i 0; i < n; i)for (int j i 1; j < n; j)if (gcd(to_string(nums[i])[0] - 0, to_string(nums[j]).back() - 0) …

人工智能技术与GIS结合的发展

个人本是GIS专业出身&#xff0c;不知名985高校本硕。工作几年后先后积累了国土空间规划、cesium开发、地理信息数据采集、地理大数据处理&#xff0c;遥感影像处理、人工智能识别&#xff0c;做过十多个500万以上的相关项目&#xff0c;有一些浅薄的经验&#xff0c;想和大家分…

tinykv project4总结

主要目标 实现mvcc和2pc, Percolator partA 将存储分为三个独立的部分&#xff0c;lock&#xff08;管理锁记录&#xff09; default(存储数据)&#xff0c;write(提交的记录)&#xff0c;提高并行性 对于lock存储&#xff0c;只要存储一份&#xff08;因为一个行同时只能有…

chatgpt赋能python:用Python编写聊天机器人:打造AI智能助手

用Python编写聊天机器人&#xff1a;打造AI智能助手 简介 聊天机器人在现代生活中越来越受欢迎。一个好的聊天机器人能够回答我们的问题、执行任务、提供娱乐&#xff0c;甚至成为我们的朋友。Python是一种强大的编程语言&#xff0c;其模块化和易学的特性使其成为开发聊天机…

GPT-4在药物发现中的作用|景联文科技

GPT-4是一种生成式AI模型&#xff0c;可以响应文本和图像&#xff0c;它代表了生成式AI可能实现的重大进步。 药物发现的最早任务之一是检索和观察与靶蛋白结合的已知分子。这可能会导致一种基于知识的筛选方法&#xff0c;人们试图通过仅检查这些分子来进行筛选。我们让GPT-4…

【MySQL数据库的表连接语句】

目录 一、连接查询1、inner join(内连接)2、left join(左连接)3、right join(右连接) 二、CREATE VIEW三、UNION取非交集的值 五、CASE六、空值(NULL) 和 无值() 的区别 一、连接查询 A表 B表 UPDATE store_info SET store_nameWashington WHERE sales300;#修改一下表里面的…