Devops之代码质量平台实践

Devops之代码质量平台实践_第1张图片

实验环境

sonarqube:8.9.8-community
docker 20.10.11
centos7虚机
win10笔记本

实验软件

链接:https://pan.baidu.com/s/1YROQTC8h89EViWp57wpYmw?pwd=xu4c
提取码:xu4c
2022.6.4-代码质量平台实践-code

Devops之代码质量平台实践_第2张图片

本节实践

  1. 实践:部署SconarQube服务端和Scanner(测试成功)-2022.5.24
  2. 实践:Scanner进行项目代码扫描(测试成功)-2022.5.24
  3. 实践:Jenkinsfile PipeLine中的代码扫描(测试成功)-2022.5.25
  4. 实践:GitLab PipeLine中的代码扫描(测试成功)-2022.5.26
  5. 实践:Jenkins插件进行扫描(测试成功)-2022.5.26
  6. 实践:SonarQube REST API(测试成功)-2022.5.27
  7. 实践:多分支代码扫描(测试成功)-2022.5.28
  8. 实践:扫描结果关联commitid(静态配置)(测试failed)-2022.6.3
  9. 实践:扫描结果关联commitid(动态配置)(测试成功)-2022.6.3
  10. 实践:控制代码扫描步骤运行(测试成功)-2022.6.4

1、SonarQube 简介

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

Devops之代码质量平台实践_第3张图片

开发人员在IDE开发代码,可以安装SonarLint插件(本地代码扫描)进行提交前代码扫描 当开发人员提交代码到版本控制系统中,自动触发jenkins进行代码扫描。

2、SonarQube平台安装配置

  • 安装包下载链接:

https://www.sonarqube.org/downloads/

Devops之代码质量平台实践_第4张图片

Devops之代码质量平台实践_第5张图片

Devops之代码质量平台实践_第6张图片

1.组件与服务组成

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

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

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

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

Devops之代码质量平台实践_第7张图片

Devops之代码质量平台实践_第8张图片

注意:这里的Embedded数据库仅仅作为实验目的

生产里一般会用pg数据库:

Devops之代码质量平台实践_第9张图片

image-20220524093922003

2.docker方式安装

  • Java (Oracle JRE 11 or OpenJDK 11) (这里一定要使用jdk11的哈哈,不然会报错……,对SonarQube8.9版本

下面是基于Docker的方式安装部署SonaQube:

## 创建数据目录
mkdir -p /data/cicd3/sonarqube/{sonarqube_conf,sonarqube_extensions,sonarqube_logs,sonarqube_data}
chmod 777 -R /data/cicd3/sonarqube/

## 运行
docker run  -itd  --name sonarqube \
    -p 9000:9000 \
    -v /data/cicd3/sonarqube/sonarqube_conf:/opt/sonarqube/conf \
    -v /data/cicd3/sonarqube/sonarqube_extensions:/opt/sonarqube/extensions \
    -v /data/cicd3/sonarqube/sonarqube_logs:/opt/sonarqube/logs \
    -v /data/cicd3/sonarqube/sonarqube_data:/opt/sonarqube/data \
    sonarqube:8.9.2-community

## 验证
docker logs -f sonarqube


## lib目录(低于SonarQube8.9.1可以配置持久化,以支持多分支插件。)

mkdir -p /data/cicd/sonarqube/sonarqube_lib
cd  /data/cicd/sonarqube/sonarqube_lib
docker cp sonarqube:/opt/sonarqube/lib/* ./

docker run  -itd  --name sonarqube \
    -p 9000:9000 \
    -v /data/cicd2/sonarqube/sonarqube_conf:/opt/sonarqube/conf \
    -v /data/cicd2/sonarqube/sonarqube_extensions:/opt/sonarqube/extensions \
    -v /data/cicd2/sonarqube/sonarqube_logs:/opt/sonarqube/logs \
    -v /data/cicd2/sonarqube/sonarqube_data:/opt/sonarqube/data \
    -v /data/cicd2/sonarqube/sonarqube_lib:/opt/sonarqube/lib \
    sonarqube:8.9.1-community

3.Terraform方式安装(本次安装方式)

实践:部署SconarQube服务端和Scanner(测试成功)-2022.5.24

Devops之代码质量平台实践_第10张图片

1️⃣ 创建数据目录:

[root@devops remote-vscode]#mkdir -p /data/devops4/sonarqube/sonarqube_conf
[root@devops remote-vscode]#mkdir -p /data/devops4/sonarqube/sonarqube_extensions
[root@devops remote-vscode]#mkdir -p /data/devops4/sonarqube/sonarqube_logs
[root@devops remote-vscode]#mkdir -p /data/devops4/sonarqube/sonarqube_data

2️⃣ 使用terraform安装:

[root@devops remote-vscode]#terraform init
[root@devops remote-vscode]#terraform plan
[root@devops remote-vscode]#terraform apply

代码参考:https://github.com/terraform-group/terraform-devops-tools/blob/master/sonarqube.tf

完整terraform代码:

main.tf

# 定义provider(main.tf)
terraform {
  required_providers {
    docker = {
      source = "kreuzwerker/docker"
      version = "~> 2.13.0"
      }
  }
}

provider "docker" {}

snoarqube.tf:

locals {
  volumes = [
    {
      container_path = "/data/devops4/sonarqube/sonarqube_conf"
      host_path      = "/opt/sonarqube/conf"
    },
    {
      container_path = "/data/devops4/sonarqube/sonarqube_extensions"
      host_path      = "/opt/sonarqube/extension"
    },
    {
      container_path = "/data/devops4/sonarqube/sonarqube_logs"
      host_path      = "/opt/sonarqube/logs"
    },
    {
      container_path = "/data/devops4/sonarqube/sonarqube_data"
      host_path      = "/opt/sonarqube/data"
    }
  ]
}

resource "docker_image" "sonar" {
  name         = "sonarqube:8.9.8-community"
  keep_locally = true //销毁时不删除本地镜像
}

resource "docker_container" "sonarqube" {
  image = docker_image.sonar.name
  name  = "sonarqube"
  ports {
    internal = 9000
    external = 9000
  }

  dynamic "volumes" {
    for_each = local.volumes
    content {
      container_path = volumes.value["container_path"]
      host_path      = volumes.value["host_path"]
    }
  }
}

3️⃣ 验证:

查看容器是否启动:

image-20220524081341935

浏览器里访问:http://172.29.9.101:9000/

默认密码是admin/admin,第一次登录需要修改默认密码,本次修改为admin/admin123。

Devops之代码质量平台实践_第11张图片

Devops之代码质量平台实践_第12张图片

安装完成。

  • 这是SonarQubeLTS 7.9.6 的默认首页:

Devops之代码质量平台实践_第13张图片

  • 这是SonarQubeLTS 8.9.8 的默认首页:

Devops之代码质量平台实践_第14张图片

4.安装中文插件

在线安装或者手动上传插件。

方法1: 在线安装:操作如下图

Devops之代码质量平台实践_第15张图片

重启server:

Devops之代码质量平台实践_第16张图片

Devops之代码质量平台实践_第17张图片

重启后,登录查看效果:

Devops之代码质量平台实践_第18张图片

Devops之代码质量平台实践_第19张图片

方法2:手动上传插件

下载好的插件包会放在downloads目录下,重启后就被放到了plugins目录下;

或者直接放在plugins目录下,重启下,也是可以的;

Devops之代码质量平台实践_第20张图片

5.手动安装插件

为什么需要手动安装? 由于不同区域的不同网络原因,可能在线源会出现下载插件失败的情况,超时。本次以中文插件为例,为大家讲述如何手动离线安装一次插件。

Caused by: org.sonar.api.utils.SonarException: Fail to download: https://github.com/xuhuisheng/sonar-l10n-zh/releases/download/sonar-l10n-zh-plugin-1.29/sonar-l10n-zh-plugin-1.29.jar (no proxy)
	at org.sonar.core.util.DefaultHttpDownloader.failToDownload(DefaultHttpDownloader.java:155)
	at org.sonar.core.util.DefaultHttpDownloader.download(DefaultHttpDownloader.java:150)
	at org.sonar.server.plugins.PluginDownloader.downloadRelease(PluginDownloader.java:152)
	at org.sonar.server.plugins.PluginDownloader.download(PluginDownloader.java:126)
	... 47 common frames omitted
Caused by: java.net.SocketTimeoutException: Read timed out
  • 阅读插件说明文档:https://github.com/xuhuisheng/sonar-l10n-zh/tree/master 找到sonarqube 版本对应的插件版本。

Devops之代码质量平台实践_第21张图片

选择Releases 找到当前SonarQube对应的版本, 我的版本是8.9.1, 所以下载 :

https://github.com/xuhuisheng/sonar-l10n-zh/releases/download/sonar-l10n-zh-plugin-8.9/sonar-l10n-zh-plugin-8.9.jar

Devops之代码质量平台实践_第22张图片

cd /data/cicd/sonarqube/sonarqube_extensions/downloads
wget https://github.com/xuhuisheng/sonar-l10n-zh/releases/download/sonar-l10n-zh-plugin-1.29/sonar-l10n-zh-plugin-1.29.jar
chmod +x sonar-l10n-zh-plugin-1.29.jar
docker restart sonarqube
  • 插件也可以直接在插件源:https://update.sonarsource.org/中下载的:

获得插件下载url:

Devops之代码质量平台实践_第23张图片

6.配置强制登录

默认所有项目都可以公开的查看,在企业内肯定要配置成私有的。只有登录后才能查看。(sonarqube8.9.1 默认已经开启了,低于该版本的可以手动开启)

Devops之代码质量平台实践_第24张图片

7.配置Scanner

scanner的类型有很多, 可以通过官网:https://docs.sonarqube.org/8.9/analysis/overview/ 获取支持的列表。

  • 变更项目代码: 可以使用构建工具进行扫描。例如maven、ant、gradle可以在配置文件中引入对应的配置。
  • 不变更项目代码配置: 可以使用Jenkins或其他平台的扩展插件, 以及使用命令行进行扫描。
  • Gradle - SonarScanner for Gradle
  • .NET - SonarScanner for .NET
  • Maven - use the SonarScanner for Maven
  • Jenkins - SonarScanner for Jenkins (Jenkins 集成插件)
  • Azure DevOps - SonarQube Extension for Azure DevOps (devops平台扩展插件)
  • Ant - SonarScanner for Ant
  • anything else (CLI) - SonarScanner(更加通用的扫描命令行)

使用更加灵活的命令行工具进行扫描, 可以在不更改项目配置的前提下进行代码扫描:

复制链接地址:

https://docs.sonarqube.org/8.9/analysis/scan/sonarscanner/

Devops之代码质量平台实践_第25张图片

(1)下载包
[root@devops ~]#wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.7.0.2747-linux.zip
[root@devops ~]#ll -h  sonar-scanner-cli-4.7.0.2747-linux.zip 
-rw-r--r-- 1 root root 42M May 24 20:09 sonar-scanner-cli-4.7.0.2747-linux.zip

(2)设置环境变量
[root@devops ~]#mkdir /usr/local/sonar-scanner
[root@devops ~]#mv sonar-scanner-cli-4.7.0.2747-linux.zip /usr/local/sonar-scanner/
[root@devops ~]#cd /usr/local/sonar-scanner/
[root@devops sonar-scanner]#unzip sonar-scanner-cli-4.7.0.2747-linux.zip
[root@devops sonar-scanner]#cd sonar-scanner-4.7.0.2747-linux/
[root@devops sonar-scanner-4.7.0.2747-linux]#echo 'export SONAR_SCANNER_HOME=/usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux' >> /etc/profile
[root@devops sonar-scanner-4.7.0.2747-linux]#echo 'export PATH=$SONAR_SCANNER_HOME/bin:$PATH' >> /etc/profile
[root@devops sonar-scanner-4.7.0.2747-linux]#source /etc/profile 

(3)验证
[root@devops sonar-scanner-4.7.0.2747-linux]#sonar-scanner -v
INFO: Scanner configuration file: /usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/conf/sonar-scanner.properties
INFO: Project root configuration file: NONE
INFO: SonarScanner 4.7.0.2747
INFO: Java 11.0.14.1 Eclipse Adoptium (64-bit)
INFO: Linux 3.10.0-957.el7.x86_64 amd64

Devops之代码质量平台实践_第26张图片

注意:

这里请注意JDK版本的问题, 默认安装的sonarscanner是使用自带的jdk。通过上面的显示可以发现是jdk1.11版本, 如果想使用系统默认的jdk版本,可以修改scanner可执行程序的配置。如下所示:

[root@devops sonar-scanner-4.7.0.2747-linux]#pwd
/usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux
[root@devops sonar-scanner-4.7.0.2747-linux]#ls
bin  conf  jre  lib
[root@devops sonar-scanner-4.7.0.2747-linux]#vim bin/sonar-scanner
### 编辑约第42行, true改成false
42 use_embedded_jre=false
43 if [ "$use_embedded_jre" = true ]; then
44   export JAVA_HOME="$sonar_scanner_home/jre"
45 fi

### 更改后,再次验证,发现变成了系统jdk版本。 更改生效。
[root@devops sonar-scanner-4.7.0.2747-linux]#sonar-scanner -v
INFO: Scanner configuration file: /usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/conf/sonar-scanner.properties
INFO: Project root configuration file: NONE
INFO: SonarScanner 4.7.0.2747
INFO: Java 1.8.0_322 Temurin (64-bit)
INFO: Linux 3.10.0-957.el7.x86_64 amd64

3、SonarScanner使用方法

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

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

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

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

# 定义唯一的关键字
sonar.projectKey=devops-hello-service

# 定义项目名称
sonar.projectName=devops-hello-service

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

# 指定sonar Server
sonar.host.url=

# 认证信息
sonar.login
sonar.password

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

文件方式:可以将扫描参数放到项目的根目录或者sonar-scanner的配置文件目录等自定义的目录中;
命令行传参则可以直接将变量传递给sonarsacnner cli -Dsonar.projectKey=xxx 。

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

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

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

扩展:Docker运行sonarscanner

docker run \
    --rm \
    -e SONAR_HOST_URL="http://${SONARQUBE_URL}" \
    -e SONAR_LOGIN="myAuthenticationToken" \
    -v "${YOUR_REPO}:/usr/src" \
    sonarsource/sonar-scanner-cli 

关于项目参数可以参考:https://docs.sonarqube.org/latest/analysis/analysis-parameters/
各种语言的扫描示例:https://docs.sonarqube.org/latest/analysis/languages/

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

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

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

[root@zeyang-nuc-service ~]# cd /data/cicd/plugin-sonar/
[root@zeyang-nuc-service plugin-sonar]# ls
sonar-go-plugin-1.6.0.719.jar            sonar-l10n-zh-plugin-1.29.jar
sonar-java-plugin-6.3.2.22818.jar        sonar-typescript-plugin-2.1.0.4359.jar
sonar-javascript-plugin-6.2.2.13315.jar
[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/
[root@zeyang-nuc-service plugin-sonar]# ls /data/cicd/sonarqube/sonarqube_extensions/downloads/
sonar-go-plugin-1.6.0.719.jar      sonar-javascript-plugin-6.2.2.13315.jar
sonar-java-plugin-6.3.2.22818.jar  sonar-typescript-plugin-2.1.0.4359.jar
[root@zeyang-nuc-service plugin-sonar]# chmod +x /data/cicd/sonarqube/sonarqube_extensions/downloads/*
[root@zeyang-nuc-service plugin-sonar]# ls /data/cicd/sonarqube/sonarqube_extensions/downloads/
sonar-go-plugin-1.6.0.719.jar      sonar-javascript-plugin-6.2.2.13315.jar
sonar-java-plugin-6.3.2.22818.jar  sonar-typescript-plugin-2.1.0.4359.jar
[root@zeyang-nuc-service plugin-sonar]# docker restart sonarqube
sonarqube

Devops之代码质量平台实践_第28张图片

1.Java项目扫描

实践:Scanner进行项目代码扫描(测试成功)-2022.5.24
![](https://img-blog.csdnimg.cn/img_convert/e8a30ac4011ea81ee8c00d7e71a377c6.png)

sonar.projectKey 指定项目的关键字
sonar.host.url指定服务器地址(可以直接在配置文件中写死),
projectName指定项目的名称,
projectVersion指定项目的版本(可以用构建时间和构建ID定义),
login指定登录用户名,
password指定登录用户密码,
projectDescription指定项目的描述信息,
links.homepage指定项目的主页(超链接),
sources指定扫描的目录,
sourceEncoding指定扫描时的编码,
java.binaries指定编译后的类文件目录(必填),
java.test.binaries指定编译后的测试类目录,
java.surefire.report指定测试报告目录。

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

自己实际测试过程:

  • 克隆devops4-maven-service项目代码到sonarscanner机器:
[root@devops ~]#mkdir sonarProject
[root@devops ~]#cd sonarProject/
[root@devops sonarProject]#git clone http://172.29.9.101/devops4/devops4-maven-service.git
Cloning into 'devops4-maven-service'...
Username for 'http://172.29.9.101': root
Password for 'http://[email protected]': 
remote: Enumerating objects: 52, done.
remote: Counting objects: 100% (18/18), done.
remote: Compressing objects: 100% (18/18), done.
remote: Total 52 (delta 9), reused 0 (delta 0), pack-reused 34
Receiving objects: 100% (52/52), 56.33 KiB | 28.16 MiB/s, done.
Resolving deltas: 100% (11/11), done.
[root@devops sonarProject]#ls
devops4-maven-service
[root@devops sonarProject]#cd devops4-maven-service/
[root@devops devops4-maven-service]#ls
build.sh  mvnw  mvnw.cmd  pom.xml  README.md  src
[root@devops devops4-maven-service]#
  • 在该项目目录下创建sonar-project.properties文件:
[root@devops devops4-maven-service]#pwd
/root/sonarProject/devops4-maven-service
[root@devops devops4-maven-service]#ls
build.sh  mvnw  mvnw.cmd  pom.xml  README.md  src
[root@devops devops4-maven-service]#vim sonar-project.properties
# 定义项目关键字
sonar.projectKey=devop4-maven-service

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

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

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

  # 认证信息
sonar.login=admin
sonar.password=admin123 
  • 进行扫描:
[root@devops devops4-maven-service]#sonar-scanner

此时会发现一个常见的报错:
Devops之代码质量平台实践_第29张图片
注意:java类型项目比较特殊,扫描时要先对其进行下编译,因为sonarqube在扫描时需要用到它编译后的类!

  • 此时再编辑下sonar-project.properties内容,并编译下后,再执行下扫描:
[root@devops devops4-maven-service]#vim sonar-project.properties
# 定义项目关键字
sonar.projectKey=devop4-maven-service

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

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

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

  # 认证信息
sonar.login=admin
sonar.password=admin123

sonar.java.binaries=target/classes 

[root@devops devops4-maven-service]#mvn clean package
[root@devops devops4-maven-service]#ls
build.sh  mvnw  mvnw.cmd  pom.xml  README.md  sonar-project.properties  src  target
[root@devops devops4-maven-service]#ls target/
classes                  demo-0.0.1-SNAPSHOT.jar.original  generated-test-sources  maven-status
demo-0.0.1-SNAPSHOT.jar  generated-sources                 maven-archiver          test-classes
[root@devops devops4-maven-service]#ls target/classes/
application.properties  com  static

再次扫描下,查看结果:
Devops之代码质量平台实践_第30张图片

[root@devops devops4-maven-service]#ls .scannerwork/
css-bundle  report-task.txt
[root@devops devops4-maven-service]#cat  .scannerwork/report-task.txt 
projectKey=devop4-maven-service
serverUrl=http://172.29.9.101:9000
serverVersion=8.9.8.54436
dashboardUrl=http://172.29.9.101:9000/dashboard?id=devop4-maven-service
ceTaskId=AYD2jY87VkQu9X-kQ24q
ceTaskUrl=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
    Devops之代码质量平台实践_第31张图片
    这里可以设置为误判
    Devops之代码质量平台实践_第32张图片
    注意:soarqube会把源代码下载下来的,因此一定要保护好sonarqube的账户信息。
    Devops之代码质量平台实践_第33张图片
    可以看到这里是1.0版本:
    Devops之代码质量平台实践_第34张图片

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

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

再次扫描下,再来看下效果:
sonar-scanner
Devops之代码质量平台实践_第35张图片
此时就可以看到新代码这里显示的bug数了:

注意:这里的sonar-project.properties文件名是不能变的!
Devops之代码质量平台实践_第36张图片

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项目扫描

Devops之代码质量平台实践_第37张图片

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.Jenkins PipeLine中的代码扫描

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

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

Devops之代码质量平台实践_第38张图片

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

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

Devops之代码质量平台实践_第39张图片

# 定义项目关键字
sonar.projectKey=devop4-maven-service

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

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

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

  # 认证信息
sonar.login=admin
sonar.password=admin123

sonar.java.binaries=target/classes
  • 先在jenkins里跑一下devops4-maven-service项目流水线,看下是否可正常跑下去:

Devops之代码质量平台实践_第40张图片

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

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

Devops之代码质量平台实践_第41张图片

Devops之代码质量平台实践_第42张图片

完整Jenkinsfile代码如下:

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

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

//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{
                    cliPath="/usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/bin"
                    sh "${cliPath}/sonar-scanner"
                }
            }
        }
    }
}
  • 这里优化下,把项目里的sonar-project.properties文件里的sonar.login和sonar.password使用jenkins的凭据功能给隐藏起来:

Devops之代码质量平台实践_第43张图片

Devops之代码质量平台实践_第44张图片

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

Devops之代码质量平台实践_第45张图片

Devops之代码质量平台实践_第46张图片

Devops之代码质量平台实践_第47张图片

withCredentials([usernamePassword(credentialsId: '79d8f75b-3733-49f4-950f-19f8480fda03', passwordVariable: 'SONAR_PASSWD', usernameVariable: 'SONAR_USER')]) {
    // some block
}
  • 将生成的代码写在jenkins共享库的jenkiinsfile里面,然后再次跑一下流水线,观察效果:
@Library("mylib@main") _     //加载共享库
import org.devops.*						// 导入库

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

//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{
                    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=${env.branchName}
                        """
                    }
                    
                }
            }
        }
    }
}

写好后提交:

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

Devops之代码质量平台实践_第48张图片

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

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

Devops之代码质量平台实践_第49张图片

Devops之代码质量平台实践_第50张图片

Devops之代码质量平台实践_第51张图片

  • 接下来再进一步扩展:

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

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

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}
        """
    }
}

编写Jenkinsfile里的代码:

@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{
                    sonar.CodeScan("${env.branchName}")
                    }
                    
                }
        }
        
    }
}

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

Devops之代码质量平台实践_第52张图片

Devops之代码质量平台实践_第53张图片

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

2.GitLab PipeLine中的代码扫描

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

Devops之代码质量平台实践_第54张图片

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

来到devops4组下:

Devops之代码质量平台实践_第55张图片

image-20220526101432510

Devops之代码质量平台实践_第56张图片

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

编辑jobs/CI.yaml文件:

Devops之代码质量平台实践_第57张图片

.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文件:

Devops之代码质量平台实践_第58张图片

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
  • 编写完成后,提交,然后就可以看到能够成功构建:

Devops之代码质量平台实践_第59张图片

Devops之代码质量平台实践_第60张图片

Devops之代码质量平台实践_第61张图片

测试完成。

3.插件方式

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

Devops之代码质量平台实践_第62张图片

Devops之代码质量平台实践_第63张图片

  • 官方参考

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

Devops之代码质量平台实践_第64张图片

  • 创建SonaQube的账户token:

Devops之代码质量平台实践_第65张图片

  • 将token保存到Jenkins凭据中:

Devops之代码质量平台实践_第66张图片

  • 在Jenkins中安装插件sonarqube scanner:

Devops之代码质量平台实践_第67张图片

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

Devops之代码质量平台实践_第68张图片

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

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

Devops之代码质量平台实践_第69张图片

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的用户方便些。

Devops之代码质量平台实践_第70张图片

Devops之代码质量平台实践_第71张图片

Devops之代码质量平台实践_第72张图片

测试完成。

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}"

Devops之代码质量平台实践_第73张图片

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调试:

Devops之代码质量平台实践_第74张图片

Devops之代码质量平台实践_第75张图片

转换cURL命令:

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

Devops之代码质量平台实践_第76张图片

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

在jenkins里添加凭据:

Devops之代码质量平台实践_第77张图片

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

Devops之代码质量平台实践_第78张图片

  • 编写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
        
    }
}

运行测试:

Devops之代码质量平台实践_第79张图片

Devops之代码质量平台实践_第80张图片

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

Devops之代码质量平台实践_第81张图片

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

2.创建项目

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

请求类型:POST

  • postman调试:

Devops之代码质量平台实践_第82张图片

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
    }
}

Devops之代码质量平台实践_第83张图片

Devops之代码质量平台实践_第84张图片

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

3.更新项目质量配置

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

请求类型:POST

postman调试:

Devops之代码质量平台实践_第85张图片

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
    }
}

运行后效果:

Devops之代码质量平台实践_第86张图片

Devops之代码质量平台实践_第87张图片

4.控制逻辑(实践)

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

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

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

Devops之代码质量平台实践_第88张图片

Devops之代码质量平台实践_第89张图片

Devops之代码质量平台实践_第90张图片

Devops之代码质量平台实践_第91张图片

自动化实现方式 :

  • 查询项目是否存在

    • 存在: 直接更新质量配置
    • 不存在: 创建空的项目然后更新质量配置
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文件里进行编辑:

Devops之代码质量平台实践_第92张图片

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代码:

Devops之代码质量平台实践_第93张图片

@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文件的配置正确:

Devops之代码质量平台实践_第94张图片

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

Devops之代码质量平台实践_第95张图片

Devops之代码质量平台实践_第96张图片

Devops之代码质量平台实践_第97张图片

符合预期。

6、其他日常使用问题

1.规则的禁用与启用

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

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

Devops之代码质量平台实践_第98张图片

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

Devops之代码质量平台实践_第99张图片

Devops之代码质量平台实践_第100张图片

创建新的规则集:

Devops之代码质量平台实践_第101张图片

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

Devops之代码质量平台实践_第102张图片

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

Devops之代码质量平台实践_第103张图片

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

Devops之代码质量平台实践_第104张图片

2.质量阈的配置

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

Devops之代码质量平台实践_第105张图片

Devops之代码质量平台实践_第106张图片

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上构建验证:

Devops之代码质量平台实践_第107张图片

Devops之代码质量平台实践_第108张图片

Devops之代码质量平台实践_第109张图片

4.多分支代码扫描

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

Devops之代码质量平台实践_第110张图片

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

FAQ: 需要先把主分支扫描一遍,不然会报错。

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

Devops之代码质量平台实践_第111张图片

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

Devops之代码质量平台实践_第112张图片

自己实际测试过程:

  • 我们可以看到这里默认是没有其他分支的:

Devops之代码质量平台实践_第113张图片

  • 官网下载插件:

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

本次找到1.8.1插件:

Devops之代码质量平台实践_第114张图片

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

Devops之代码质量平台实践_第115张图片

  • 配置:
#(1)进下载的插件上传到sonarqube容器里:
[root@devops ~]#ll -h  sonarqube-community-branch-plugin-1.8.1.jar 
-rw-r--r-- 1 root root 12M May 28 11:11 sonarqube-community-branch-plugin-1.8.1.jar
[root@devops ~]#docker cp sonarqube-community-branch-plugin-1.8.1.jar sonarqube:/opt/sonarqube/extensions/plugins

[root@devops ~]#docker exec -it sonarqube bash
bash-5.1# ls extensions/plugins/
README.txt                                   sonarqube-community-branch-plugin-1.8.1.jar
sonar-l10n-zh-plugin-8.9.jar
bash-5.1# 

#(2)配置/opt/sonarqube/conf/sonar.properties 文件
bash-5.1# pwd
/opt/sonarqube
bash-5.1# ls
COPYING                  data                     extensions               temp
bin                      dependency-license.json  lib                      web
conf                     elasticsearch            logs
bash-5.1# cd conf/
bash-5.1# ls
sonar.properties  wrapper.conf
bash-5.1# vim 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

#(3)配置完后,退出重启容器:
[root@devops ~]#docker restart sonarqube

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

Devops之代码质量平台实践_第116张图片

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

Devops之代码质量平台实践_第117张图片

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

Devops之代码质量平台实践_第118张图片

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

Devops之代码质量平台实践_第119张图片

Devops之代码质量平台实践_第120张图片

符合预期,测试结束。

5.扫描结果关联commitid

提前装好插件: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

Devops之代码质量平台实践_第121张图片

-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

Devops之代码质量平台实践_第122张图片

1.静态配置

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

Devops之代码质量平台实践_第123张图片

自己实际测试过程:

  • 安装插件:

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

Devops之代码质量平台实践_第124张图片

#(1)上传插件
[root@devops ~]#ll -h sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar
-rw-r--r-- 1 root root 9.9M May 28 15:43 sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar
[root@devops ~]#docker cp sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar sonarqube:/opt/sonarqube/extensions/plugins

#(2)重启sonarqube容器
[root@devops ~]#docker restart sonarqube
sonarqube
  • 在gitlab上生成token:

Devops之代码质量平台实践_第125张图片

Devops之代码质量平台实践_第126张图片

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

找一次commit信息:并拷贝commitid

5abc33244088898bdfb494ed0a3fc134ab0f5c80

Devops之代码质量平台实践_第127张图片

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

Devops之代码质量平台实践_第128张图片

核对该项目的相关信息:

Devops之代码质量平台实践_第129张图片

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

Devops之代码质量平台实践_第130张图片

Devops之代码质量平台实践_第131张图片

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

好奇怪:

Devops之代码质量平台实践_第132张图片

老师这里是有效果的:

Devops之代码质量平台实践_第133张图片

Devops之代码质量平台实践_第134张图片

好奇怪……

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

2.动态配置

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

Devops之代码质量平台实践_第135张图片

Devops之代码质量平台实践_第136张图片

  • 那么,接下来配置下动态的,看下效果:
-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的项目是保持一致的:

Devops之代码质量平台实践_第137张图片

Devops之代码质量平台实践_第138张图片

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

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

Devops之代码质量平台实践_第139张图片

这里直接在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"]
            }
        }
    }
}

运行后效果如下:

Devops之代码质量平台实践_第140张图片

符合预期,但是注意下,这个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)
                    }
                    
                }
        }
        
    }
}

Devops之代码质量平台实践_第141张图片

GitLab.groovy

Devops之代码质量平台实践_第142张图片

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项目运行流水线,观察效果:

可以看到,构建成功:

Devops之代码质量平台实践_第143张图片

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

Devops之代码质量平台实践_第144张图片

符合预期,测试结束。

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

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

Devops之代码质量平台实践_第145张图片

Devops之代码质量平台实践_第146张图片

自己测试过程:

实践:控制代码扫描步骤运行(测试成功)-2022.6.4
  • 修改jenkins共享库里的代码:

修改Jenkinsfile里的如下代码:

Devops之代码质量平台实践_第147张图片

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

Devops之代码质量平台实践_第148张图片

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

Devops之代码质量平台实践_第149张图片

Devops之代码质量平台实践_第150张图片

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

测试结束。

7、GitLabCI-实践

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

  • 编写一个python脚本:
import os 
import requests
import json
import sys


class SonarQube(object):
    def __init__(self, project_name, lang, profile_name, cmds):
        self.server_api = "http://192.168.1.200:9000/api/"
        self.auth_token = "YWRtaW46YWRtaW4xMjM="
        self.project_name = project_name
        self.lang = lang
        self.profile_name = profile_name
        self.cmds = cmds

    def http_req(self, method, apiUrl):
        url = self.server_api + apiUrl
        payload={}
        headers = {
          'Authorization': 'Basic ' + self.auth_token
        }
        response = requests.request(method, url, headers=headers, data=payload)
        print(response.text)

        if response.text != "":
            data = json.loads(response.text)
            return data
        return {}


    def SearchProject(self):
        """查找项目"""
        url = "projects/search?projects=" + self.project_name
        response = self.http_req("GET", url)
        if response["paging"]["total"] == 0:
            return False
        return True

    def CreateProject(self):
        """创建项目"""
        apiUrl = "projects/create?name={0}&project={1}".format(self.project_name, self.project_name)
        response = self.http_req("POST", apiUrl)
        try:
            if response["project"]["key"] == self.project_name:
                return True 
        except Exception as e :
            print(e)
            print(response["errors"])
            return False

    def UpdateQualityProfiles(self):
        apiUrl = "qualityprofiles/add_project?language={0}&project={1}&qualityProfile={2}".format(
            self.lang, self.project_name, self.profile_name)

        response = self.http_req("POST", apiUrl)

        try :
            print("ERROR: UpdateQualityProfiles{0}...".format(response["errors"]))
            return False
        except Exception as e :
            print(e)
            print("SUCCESS: UpdateQualityProfiles {0} > {1} > ${2}".format(
                self.lang, self.project_name, self.profile_name))
            return True
            

    def SonarScan(self):
        result = os.system(self.cmds)
        if result == 0:
            return True
        return False


    def run(self):
        if not self.SearchProject():
            self.CreateProject()
        self.UpdateQualityProfiles()
        return self.SonarScan()

if __name__ == '__main__':
    lang = sys.argv[1]
    profile_name = sys.argv[2]
    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:]
    print(CI_PROJECT_NAME)
    sonarcmds = """
sonar-scanner \
-Dsonar.host.url=http://192.168.1.200:9000 \
-Dsonar.projectKey={0} \
-Dsonar.projectName={0} \
-Dsonar.projectVersion={1} \
-Dsonar.login={2} \
-Dsonar.ws.timeout=30 \
-Dsonar.projectDescription={3}  \
-Dsonar.links.homepage={4} \
-Dsonar.links.ci={5} \
-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 \
-Dsonar.gitlab.commit_sha={1} \
-Dsonar.gitlab.ref_name={6} \
-Dsonar.gitlab.project_id={7} \
-Dsonar.dynamicAnalysis=reuseReports \
-Dsonar.gitlab.failure_notification_mode=nothing \
-Dsonar.gitlab.url={8} \
-Dsonar.gitlab.user_token={9} \
-Dsonar.gitlab.api_version=v4
""".format( 
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)
     
    result = SonarQube(CI_PROJECT_NAME, lang, profile_name, sonarcmds ).run()
    print(result)
  • 脚本调用:
python3 sonarqube.py  \
"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
include:
  - project: 'devops03/devops03-gitlabci-lib'
    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: "mvn clean package -DskipTests -s settings.xml"  ## 构建命令
  TEST_SHELL: "mvn test -s settings.xml"                        ## 测试命令
  ARTIFACT_PATH: "target/*.jar"                                 ## 制品路径
  TEST_REPORTS: "target/surefire-reports/TEST-*.xml"            ## 测试报告

stages:
  - build
  - test
  - sonarscan

pipelineInit:
  extends: 
    - .pipelineInit

cibuild:
  extends:
    - .cibuild

citest:
  extends:
    - .citest
sonarscan:
  tags:
    - build 
  stage: sonarscan
  script:
    |- 
      curl "http://192.168.1.200/devops03/devops03-gitlabci-lib/-/raw/main/utils/SonarQube.py" \
      -o sonarqube.py -s
      python sonarqube.py  "java" ${CI_PROJECT_ROOT_NAMESPACE} ${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} 
       

自己本次使用gitlabci测试

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

Devops之代码质量平台实践_第151张图片

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

devops4-gitlablib-service

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

Devops之代码质量平台实践_第152张图片

修改jobs/CI.yml

这里找一下gitlab的变量:

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

Devops之代码质量平台实践_第153张图片

image-20220604101041153

image-20220604101516395

  • 在devops4组里配置token:

MhEV52bNpbUnnSfNg1nc

Devops之代码质量平台实践_第154张图片

  • 最终配置如下:

Devops之代码质量平台实践_第155张图片

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

Devops之代码质量平台实践_第156张图片

Devops之代码质量平台实践_第157张图片

Devops之代码质量平台实践_第158张图片

符合预期,测试结束。

8、SonarQube 插件

  • 官网: http://sonarqube.org
  • 中文插件: https://github.com/xuhuisheng/sonar-l10n-zh/tree/master
  • scanner下载:https://docs.sonarqube.org/8.9/analysis/overview/
  • 插件源:https://update.sonarsource.org/
  • API文档: http://192.168.1.200:9000/web_api
  • 多分支插件: https://github.com/mc1arke/sonarqube-community-branch-plugin/releases
  • 关联提交:https://github.com/gabrie-allaigre/sonar-gitlab-plugin/tree/4.1.0-SNAPSHOT

关于我

我的博客主旨:

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

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

Devops之代码质量平台实践_第159张图片

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

Devops之代码质量平台实践_第160张图片

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

Devops之代码质量平台实践_第161张图片

博客
www.onlyyou520.com

知乎

https://www.zhihu.com/people/foryouone

Devops之代码质量平台实践_第162张图片

最后

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

你可能感兴趣的:(devops)