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

命令行方式


流水线中添加代码扫描阶段, 然后在​​script​​标签中定义一段脚本。 (其实这段脚本就是我们手动在服务器上面执行的sonar-scanner的命令和参数组成的)【可以先运行该段代码确保扫描成功,然后进一步优化

   stage("SonarScan"){
steps{
script{
sh """
sonar-scanner \
-Dsonar.projectKey=demo-devops-service \
-Dsonar.projectName=demo-devops-service \
-Dsonar.sources=src \
-Dsonar.host.url=http://192.168.1.200:9000 \
-Dsonar.login=0809881d71f2b06b64786ae3f81a9acf22078e8b \
-Dsonar.projectVersion=1.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 \
-Dsonar.java.binaries=target/classes \
-Dsonar.java.test.binaries=target/test-classes \
-Dsonar.java.surefire.report=target/surefire-reports
"""
}
}
}
}

通过上面的代码可以发现一些问题:

  • 扫描参数值都是写死的,无法使其他项目通用。
  • 代码中存在敏感数据信息。

既然想通用,就要制定规范。 例如:

  • 我们统一使用Jenkins的作业名称做为SonarQube项目名称。
  • 将Sonar在进行扫描时用到的认证信息,也存储到Jenkins的系统凭据中(Secret Text类型),避免流水线中存在敏感信息。然后使用​withCredentials​​将凭据的值内容赋值给变量​​SONAR_TOKEN​​引用。
  • 使用​BUILD_NUMBER​作为sonarqube项目版本(可以使用时间戳、版本号、commitid)。
  • -Dsonar.links.ci=${BUILD_URL}​ SonarQube的扩展链接, 方便在系统中跳转。
  • -Dsonar.links.homepage=${env.srcUrl} ​ SonarQube的扩展链接, 方便在系统中跳转。

优化后:

// 凭据列表
credentials = ["sonar" : '06bf5ee4-f571-4fe4-9b52-d17190ce54e5']

//服务器列表
servers = ["sonar": 'http://192.168.1.200:9000']

pipeline {

...

stage("CodeScan"){
steps{
script {
withCredentials([string(credentialsId: "${credentials['sonar']}", variable: 'SONAR_TOKEN')]) {
sh """
sonar-scanner \
-Dsonar.projectKey=${JOB_NAME.split('/')[-1]} \
-Dsonar.projectName=${JOB_NAME.split('/')[-1]} \
-Dsonar.sources=src \
-Dsonar.host.url=${servers['sonar']} \
-Dsonar.login=${SONAR_TOKEN} \
-Dsonar.projectVersion=${BUILD_NUMBER} \
-Dsonar.ws.timeout=30 \
-Dsonar.projectDescription="my first project!" \
-Dsonar.links.homepage=${env.srcUrl} \
-Dsonar.links.ci=${BUILD_URL} \
-Dsonar.sourceEncoding=UTF-8 \
-Dsonar.java.binaries=target/classes \
-Dsonar.java.test.binaries=target/test-classes \
-Dsonar.java.surefire.report=target/surefire-reports
"""
}
}
}
}
}

上面我们完成了,一个Java类型项目的扫描参数设置。 但是是否想过在企业中存在多种语言类型的项目? 此时我们要让代码扫描变的更加灵活一些,支持多种类型的代码扫描。

  • 根据不同的构建工具,使用不同的代码扫描参数;
  • 例如: maven 对应java类型项目, npm对应前端类型项目

 最终将上述代码,纳入共享库。创建sonar.groovy。

Jenkins SonarQube CI 流水线集成 命令行方式优化_java

Jenkins SonarQube CI 流水线集成 命令行方式优化_java_02

Jenkins SonarQube CI 流水线集成 命令行方式优化_sonarqube_03

package org.devops

def scanner(buildType,token,projectDescription,srcUrl){
switch(buildType){
case "maven":
sh """
sonar-scanner \
-Dsonar.host.url=http://139.198.166.235:9000 \
-Dsonar.projectKey=${env.JOB_NAME} \
-Dsonar.projectName=${env.JOB_NAME} \
-Dsonar.projectVersion=${env.BUILD_NUMBER} \
-Dsonar.login=${token} \
-Dsonar.ws.timeout=30 \
-Dsonar.projectDescription="my first project!" \
-Dsonar.links.homepage=${env.srcUrl} \
-Dsonar.links.ci=${env.BUILD_URL} \
-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
"""
break
case "npm":
sh """
sonar-scanner \
-Dsonar.projectKey=${env.JOB_NAME} \
-Dsonar.projectName=${env.JOB_NAME} \
-Dsonar.sources=src \
-Dsonar.host.url=http://139.198.166.235:9000 \
-Dsonar.login=${token} \
-Dsonar.projectVersion=${env.BUILD_NUMBER} \
-Dsonar.ws.timeout=30 \
-Dsonar.projectDescription="my first project!" \
-Dsonar.links.homepage=${env.srcUrl} \
-Dsonar.links.ci=${env.BUILD_URL} \
-Dsonar.sourceEncoding=UTF-8

"""
break
default:
println("sonar error !")
}
}

Jenkinsfile内容: 

@Library("devopslib@main") _

def project = new org.devops.build()
def sonar = new org.devops.sonarquebscanner()

def buildTools = ["maven": "/usr/local/apache-maven-3.8.1"]
def credentials = ["devops-maven-sonarqube": "f8b33d17-c1cf-428e-aa31-99d4038e59d0"]

String buildType = "${env.buildType}"
String projectDescription = "this is maven project"

currentBuild.description = "maven project"


pipeline {

agent {
label 'build'
}

stages {
stage('CheckOut') {
steps {
checkout([$class: 'GitSCM',
branches: [[name: "${branchName}"]],
extensions: [], userRemoteConfigs:
[[credentialsId: "${credentialsId}",
url: "${srcUrl}"]]])
}
}

stage('Build'){
steps{
script{
project.build(buildType,buildTools)
}
}
}

stage("UnitTest"){
steps{
script{
sh "${buildTools["maven"]}/bin/mvn test"
}
}
post{
success{
script{
junit 'target/surefire-reports/*.xml'
}
}
}
}
stage('CodeScan'){
steps{
script{
withCredentials([string(credentialsId: "${credentials['devops-maven-sonarqube']}", variable: 'token')]) {

sonar.scanner(buildType,token,projectDescription,srcUrl)
}
}
}
}
}
}