Jenkins Pipeline+SonarQube+Python集成钉钉自动通知(webhook版)
off999 2024-10-11 14:04 92 浏览 0 评论
一、前言
SonarQube 最需要的功能之一是能够在质量未达到预期水平时使通知或构建失败。我们知道在 SonarQube 中具有质量阀的内置概念,在上文我们是试图通过在主动等待其执行结束来获取扫描结果功能。但该解决方案并不是最好的,这意味着Jenkins 将“等待”忙碌,并且必须这个时间可控。
实现此目的的最简单的模式是释放 Jenkins 执行程序,并在执行完成时让 SonarQube 发送通知。然后,将恢复 Jenkins 作业,并采取适当的措施(不仅将作业标记为失败,而且还可以发送通知)。
由于自 SonarQube 6.2 后引入的 webhook 功能,所有这些现在都可以实现。我们可以利用Jenkins Pipeline 功能,该功能允许在不占用执行程序的情况下执行作业逻辑的某些部分。
让我们来看看它是怎么实现的。
二、准备工作
- Jenkins、SonarQube 服务已经搭建完成
- Jenkins 安装 sonar插件 SonarQube Scanner for Jenkins
- 版本:Jenkins 2.164.3,SonarQube 7.4
三、配置
具体步骤如下: (1)Jenkins 配置 SonarQube 插件
(2)SonarQube 设置 webhook,不同的代码规模的项目,分析过程的耗时是不一样的。所以当分析完成后,由 SonarQube 主动通知 Jenkins。 设置方法:进入 SonarQube Administration -> 配置 -> 网络调用
四、使用Pipeline构建
1、Pipeline的介绍
Pipeline 也就是构建流水线,对于我们来说,最好的解释是:使用代码来控制项目的构建、测试、部署等。 使用它的好处有很多,包括但不限于:
- 使用 Pipeline 可以非常灵活地控制整个构建过程
- 可以清楚地知道每个阶段使用的时间,方便优化
- 构建出错,使用 stageView 可以快速定位出错的阶段
- 一个 job 可以搞定整个构建,方便管理和维护等
2、新建Pipeline项目
建一个 Pipeline 项目,写入 Pipeline 的构建脚本,就像下面这样
3、job UI 界面(参数化构建)
在配置 job 的时候,选择参数化构建过程,传入项目仓库地址、分支、等等。还可以增加更多的参数 ,这些参数的特点是,可能需要经常修改,比如灵活选择构建的代码分支。
4、Pipeline脚本
SonarQube 提供了可以使用两个 SonarQube 关键字 “withSonarQubeEnv” 和 “waitForQualityGate” 来配置管道作业。在 Jenkins 全局配置中配置的连接详细信息将自动传递到扫描器。
如果你的 credentialId 不想使用全局配置中定义的那个,则可以覆盖。
以下是每个扫描器的一些示例,假设在 linux 务器上运行,并且已配置名为“ My SonarQube Server” 的服务器以及必需的扫描工具。如果在Windows服务器上运行,则只需替换 sh 为 bat。
分析 .NET 项目声明式脚本:
pipeline {
agent any
//变量定义
environment {
_workspace = "${env.WORKSPACE}"
_projectName = "${env.JOB_NAME}"
_BUILD_NUMBER = "${env.BUILD_NUMBER}"
_ScannerMsBuildHome = "C:\\Users\\htsd\\Downloads\\sonar-scanner-msbuild-4.6.1.2049-net46"
_MSBuildHome = "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\MSBuild\\15.0\\Bin\\amd64"
}
stages {
stage('Checkout Code'){//从git仓库中检出代码
steps {
git branch: "${BRANCH}",credentialsId: '40c624a3-b7c6-4b51-830b-2295edc3ffbd', url: "${REPO_URL}"
}
}
stage('Build & SonarQube analysis') {
steps{
withSonarQubeEnv('SonarQube7.4') {
// Due to SONARMSBRU-307 value of sonar.host.url and credentials should be passed on command line
echo "_ScannerMsBuildHome:${_ScannerMsBuildHome}"
echo "_MSBuildHome:${_MSBuildHome}"
bat "${_ScannerMsBuildHome}\\SonarScanner.MSBuild.exe begin /k:${_projectName} /n:${_projectName} /v:${_BUILD_NUMBER} /d:sonar.host.url=%SONAR_HOST_URL% /d:sonar.login=%SONAR_AUTH_TOKEN% /d:sonar.scm.provider=True"
bat "\"${_MSBuildHome}\\MSBuild.exe\" Project.sln /t:Rebuild"
bat "${_ScannerMsBuildHome}\\SonarScanner.MSBuild.exe end /d:sonar.login=%SONAR_AUTH_TOKEN%"
} // SonarQube taskId自动附加到pipeline上下文
}
}
// 不需要占用节点
stage("Quality Gate") {
steps{
timeout(time: 1, unit: 'HOURS') { // 万一发生错误,pipeline 将在超时后被终止
waitForQualityGate abortPipeline: true // 告诉 Jenkins 等待 SonarQube 返回的分析结果。当 abortPipeline=true,表示质量不合格,将 pipeline 状态设置为 UNSTABLE。
}
}
}
}
post {
always {
//发送钉钉通知
echo 'Dingtalk Notification'
bat "python D:\\WorkSpace-new\\pipline\\VBI-notification.py"
}
}
}参数解释:
sonar.projectKey:项目key (必填项)
sonar.projectName:项目名称(必填项)
sonar.projectVersion:项目版本(必填项)
sonar.sources:源码位置(相对路径)
sonar.java.binaries:编译后的class位置(必填项,相对路径同上)
sonar.exclusions:排除的扫描的文件路径
sonar.host.url:SonarQube 地址
sonar.login:SonarQube生成的token
命令行分析其他项目声明式脚本 :
pipeline {
agent any
environment {
_workspace = "${env.WORKSPACE}"
_projectName = "${env.JOB_NAME}"
_BUILD_NUMBER = "${env.BUILD_NUMBER}"
_scannerHome = "C:\\sonar-scanner-cli-3.3.0.1492-windows\\sonar-scanner-3.3.0.1492-windows\\bin"
}
stages {
stage('Checkout Code'){//从git仓库中检出代码
steps {
git branch: "${BRANCH}",credentialsId: '40c624a3-b7c6-4b51-830b-2295edc3ffbd', url: "${REPO_URL}"
}
}
stage('SonarQube analysis') {
steps{
withSonarQubeEnv('SonarQube7.4') {
// Due to SONARMSBRU-307 value of sonar.host.url and credentials should be passed on command line
echo "_scannerHome:${_scannerHome}"
bat "${_scannerHome}\\sonar-scanner.bat -Dsonar.projectName=${_projectName} -Dsonar.sources=. -Dsonar.projectKey=${_projectName} -Dsonar.projectVersion=${_BUILD_NUMBER} -Dsonar.login=%SONAR_AUTH_TOKEN% -Dsonar.scm.provider=True"
} // SonarQube taskId 自动附加到 pipeline 上下文
}
}
// 不需要占用节点
stage("Quality Gate") {
steps{
timeout(time: 1, unit: 'HOURS') { // 万一发生错误,pipeline 将在超时后被终止
waitForQualityGate abortPipeline: true // 告诉 Jenkins 等待 SonarQube 返回的分析结果。当abortPipeline=true,表示质量不合格,将pipeline状态设置为UNSTABLE。
}
}
}
}
post {
always {
//发送钉钉通知
echo 'Dingtalk Notification'
bat "python D:\\WorkSpace-new\\pipline\\VBI-notification.py"
}
}
}一些官方的示例:
SonarScanner for MSBuild:
node {
stage('SCM') {
git 'https://github.com/foo/bar.git'
}
stage('Build + SonarQube analysis') {
def sqScannerMsBuildHome = tool 'Scanner for MSBuild 4.6'
withSonarQubeEnv('My SonarQube Server') {
bat "${sqScannerMsBuildHome}\\SonarQube.Scanner.MSBuild.exe begin /k:myKey"
bat 'MSBuild.exe /t:Rebuild'
bat "${sqScannerMsBuildHome}\\SonarQube.Scanner.MSBuild.exe end"
}
}
}SonarScanner:
node {
stage('SCM') {
git 'https://github.com/foo/bar.git'
}
stage('SonarQube analysis') {
def scannerHome = tool 'SonarScanner 4.0';
withSonarQubeEnv('My SonarQube Server') { // If you have configured more than one global server connection, you can specify its name
sh "${scannerHome}/bin/sonar-scanner"
}
}
}SonarScanner for Gradle:
node {
stage('SCM') {
git 'https://github.com/foo/bar.git'
}
stage('SonarQube analysis') {
withSonarQubeEnv() { // Will pick the global server connection you have configured
sh './gradlew sonarqube'
}
}
}SonarScanner for Maven:
node {
stage('SCM') {
git 'https://github.com/foo/bar.git'
}
stage('SonarQube analysis') {
withSonarQubeEnv(credentialsId: 'f225455e-ea59-40fa-8af7-08176e86507a', installationName: 'My SonarQube Server') { // You can override the credential to be used
sh 'mvn org.sonarsource.scanner.maven:sonar-maven-plugin:3.6.0.1398:sonar'
}
}
}暂停job,直到计算出质量阀状态:
node {
stage('SCM') {
git 'https://github.com/foo/bar.git'
}
stage('SonarQube analysis') {
withSonarQubeEnv('My SonarQube Server') {
sh 'mvn clean package sonar:sonar'
} // submitted SonarQube taskId is automatically attached to the pipeline context
}
}
// No need to occupy a node
stage("Quality Gate"){
timeout(time: 1, unit: 'HOURS') { // Just in case something goes wrong, pipeline will be killed after a timeout
def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv
if (qg.status != 'OK') {
error "Pipeline aborted due to quality gate failure: ${qg.status}"
}
}
}声明式脚本:
pipeline {
agent any
stages {
stage('SCM') {
steps {
git url: 'https://github.com/foo/bar.git'
}
}
stage('build && SonarQube analysis') {
steps {
withSonarQubeEnv('My SonarQube Server') {
// Optionally use a Maven environment you've configured already
withMaven(maven:'Maven 3.5') {
sh 'mvn clean package sonar:sonar'
}
}
}
}
stage("Quality Gate") {
steps {
timeout(time: 1, unit: 'HOURS') {
// Parameter indicates whether to set pipeline to UNSTABLE if Quality Gate fails
// true = set pipeline to UNSTABLE, false = don't
waitForQualityGate abortPipeline: true
}
}
}
}
}如果要在同一 job 中运行多个分析并使用 waitForQualityGate,则必须按顺序进行所有操作: 声明式脚本:
pipeline {
agent any
stages {
stage('SonarQube analysis 1') {
steps {
sh 'mvn clean package sonar:sonar'
}
}
stage("Quality Gate 1") {
steps {
waitForQualityGate abortPipeline: true
}
}
stage('SonarQube analysis 2') {
steps {
sh 'gradle sonarqube'
}
}
stage("Quality Gate 2") {
steps {
waitForQualityGate abortPipeline: true
}
}
}
}5、钉钉通知
5.1、依赖包
pip3 install configparser
pip3 install DingtalkChatbot
pip3 install requests
pip3 install python-jenkins
pip3 install json2625.2、脚本
# coding=utf-8
'''
@author: zuozewei
@file: notification.py
@time: 2019/5/10 18:00
@description:dingTalk通知类
'''
import os
import jenkins
import configparser
import requests
import json
import time
from dingtalkchatbot.chatbot import DingtalkChatbot
from jsonpath import jsonpath
# 获取Jenkins变量
JOB_NAME = str(os.getenv("JOB_NAME"))
BUILD_URL = str(os.getenv("BUILD_URL")) + "console"
BUILD_NUMBER = str(os.getenv("BUILD_NUMBER"))
# 连接jenkins
server = jenkins.Jenkins(url="http://xxx.xxx.xxx.xxxx:8080", username='xxxx', password="xxxx")
def sonarNotification():
bug = ''
leak = ''
code_smell = ''
coverage = ''
density = ''
status = ''
title = 'xxxx代码扫描通知'
dingText = ''
SonarQube_URL = 'http://xxx.xxx.xxx.xxxx:9088/dashboard?id=' + JOB_NAME
# sonar API
sonar_Url = 'http://xxx.xxx.xxx.xxxx:9088/api/measures/search?projectKeys=' + JOB_NAME + \
'&metricKeys=alert_status%2Cbugs%2Creliability_rating%2Cvulnerabilities%2Csecurity_rating%2Ccode_smells%2Csqale_rating%2Cduplicated_lines_density%2Ccoverage%2Cncloc%2Cncloc_language_distribution'
# 获取sonar指定项目结果
resopnse = requests.get(sonar_Url).text
# 转换成josn
result = json.loads(resopnse)
# 解析sonar json结果
for item in result['measures']:
if item['metric'] == "bugs":
bug = item['value']
elif item['metric'] == "vulnerabilities":
leak = item['value']
elif item['metric'] == 'code_smells':
code_smell = item['value']
elif item['metric'] == 'coverage':
coverage = item['value']
elif item['metric'] == 'duplicated_lines_density':
density = item['value']
elif item['metric'] == 'alert_status':
status = item['value']
print('【Status】:' + status)
else:
pass
textFail = '#### ' + JOB_NAME + ' - CodeScan # ' + BUILD_NUMBER + ' \n' + \
'##### <font color=#FF0000 size=6 face="黑体">新代码质量: ' + status + '</font> \n' + \
'##### **版本类型**: ' + '开发版' + '\n' + \
'##### **Bug数**: ' + bug + '个 \n' + \
'##### **漏洞数**: ' + leak + '个 \n' + \
'##### **可能存在问题代码**: ' + code_smell + '行 \n' + \
'##### **覆盖率**: ' + coverage + '% \n' + \
'##### **重复率**: ' + density + '% \n' + \
'##### **SonarQube**: [查看详情](' + SonarQube_URL + ') \n' + \
'##### **关注人**: @158xxxx3364 \n' + \
'> ###### xxxx技术团队 \n'
textSuccess = '#### ' + JOB_NAME + ' - CodeScan # ' + BUILD_NUMBER + ' \n' + \
'##### **新代码质量**: ' + status + '\n' + \
'##### **版本类型**: ' + '开发版' + '\n' + \
'##### **Bug数**: ' + bug + '个 \n' + \
'##### **漏洞数**: ' + leak + '个 \n' + \
'##### **可能存在问题代码**: ' + code_smell + '行 \n' + \
'##### **覆盖率**: ' + coverage + '% \n' + \
'##### **重复率**: ' + density + '% \n' + \
'##### **SonarQube**: [查看详情](' + SonarQube_URL + ') \n' + \
'> ###### xxxx技术团队 \n '
# 判断新代码质量阀状态
if status == 'ERROR':
dingText = textFail
elif status == 'OK':
dingText = textSuccess
sendding(title, dingText)
def sendding(title, content):
at_mobiles = ['186xxxx2487','158xxxx3364']
Dingtalk_access_token = 'https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxx'
# 初始化机器人小丁
xiaoding = DingtalkChatbot(Dingtalk_access_token)
# Markdown消息@指定用户
xiaoding.send_markdown(title=title, text=content, at_mobiles=at_mobiles)
if __name__ == "__main__":
sonarNotification()5.3、通知效果
五、小结
我们也可以把一个 Pipeline 构建做成 Jenkinsfile 通过git管理,带来的好处如下:
- 方便多个人维护构建CI,避免代码被覆盖
- 方便构建 job 的版本管理,比如要修复某个已经发布的版本,可以很方便切换到发布版本时候用的 Pipeline 脚本版本
当然,Pipeline也存在一些弊端,比如:
- 语法不够友好,但好在 Jenkins 提供了一个比较强大的帮助工具(Pipeline Syntax),可以结合 vscode ide进行开发
- 代码测试繁琐,没有本地运行环境,每次测试都需要提交运行一个 job,等等
相关代码:
- https://github.com/zuozewei/blog-example/tree/master/Jenkins-ci/pipeline-sonarqube-python-dingtalk-notifications-webhook
参考资料:
- [1]: https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-jenkins
- [2]: Jenkins的Pipeline脚本在美团餐饮SaaS中的实践
相关推荐
- win10一定要创建账户吗(win10需要创建microsoft账户吗)
-
win10系统安装不需要申请微软账号。如果是在安装win10的过程中,则使用本地账户登录,从安装主要步骤完成之后进入后续设置阶段开始,步骤如下:1、首先就是要输入产品密钥,或者点击左下角“以后再说”。...
- win10显示已禁用输入法(w10系统已禁用输入法)
-
在使用win10的过程中,有时候利用第三方软件过度优化开机启动项目就容易导致win10无法打开输入法问题,这个情况是由于ctfmon程序无法正常启动所致,一般表现在电脑桌面右下角显示已禁用ime的提示...
- windows pad(windowspad官方网站入口)
-
平板电脑安装windows方法如下1、首先,下载并安装U启动PE制作工具,这里要特别注意的是,要下载装机版的。2、点开PE制作工具的主界面,插入U盘,等待U盘被制作工具识别出来后。3、点击归还空间,然...
- 为什么电脑一开机就死机(为什么电脑一开机就死机重启)
-
一、软件问题: 1、导致死机的一个重要原因就是病毒程序的入侵。大家都知道,病毒程序是一种会破坏计算机软件系统,并占用极大的系统资源的一种恶意攻击程序,它会给计算机本身的软件造成很大的伤害。死机时的首...
- 0x0000007a蓝屏解救方法win7
-
0x0000007A说明是内存或虚拟内存(硬盘)的问题,你可以按顺序尝试如下操作:1、更改虚拟内存页面文件位置:我的电脑→右键→属性→高级→性能设置→高级→虚拟内存更改→取消原来选择的驱动器(默认在C...
- 系统小说排行榜完本经典之作
-
超级兑换系统超级修仙超级客栈系统貌似高手在异界重生之修仙系统超级修仙系统异界之兑换成圣(贱圣VS奸神)+超级兑换(火山飞狐)+穿越之无敌兑换(开心小帅)+兑换器修仙(轻舞流芒)+...
- 手机能修复u盘吗(手机修复u盘工具下载)
-
1.在手机上可以恢复u盘,当手机SD卡或U盘插入电脑中时,如果提示“文件或目录损坏且无法读取”的信息时,我们首先需要对手机SD卡或U盘进行目录修复操作。插入待修复的U盘,打开“我的电脑”,找到Sd卡...
- 怎么查电脑显卡的信息(电脑怎么查看显卡信息)
-
要查看电脑的显卡信息,可以按照以下步骤进行操作:1.使用快捷键Win+R打开“运行”对话框。2.在运行对话框中输入“dxdiag”并点击“确定”按钮,打开“DirectX诊断工具”。3....
- 电脑上找不到输入法怎么办(电脑中找不到输入法)
-
如果电脑上不显示输入法,您可以尝试以下解决方法:1.检查输入法设置:首先,您可以检查电脑的输入法设置。在Windows系统中,您可以点击任务栏右下角的输入法图标(一般为字母或语言标志),然后选择“显...
- win10系统本地连接在哪里(window10的本地连接在哪)
-
要找到本地连接,可以按照以下步骤在Windows10系统中进行:1.点击“开始”菜单,然后选择“设置”(齿轮图标)。2.在设置窗口中选择“网络和Internet”选项。3.在“网络和Inter...
- win10有32位版本吗(win10还有32位的吗)
-
64位版本好。32位的操作系统处理数据的能力较慢,支持的内存小,并且只支持基于32位的软件,不能运行64位的软件。64位的操作系统处理数据的能力较快,支持的内存较大,能运行32位的软件,也能运行6...
- 账号密码大全真的(各种账号密码)
-
英雄号,是4399官方版本的账号:1973024549密码:123456这是自己的,不想玩了,送给看到的有缘人吧^o^1、默认的机顶盒密码6321,也可以进行更改,方法:首先,通过搜索“中国电信”...
- windows server2019(windowsserver2019密钥激活码)
-
WindowsServer2019那是给服务器用的系统。服务器的作用是计算数据,而不是图像处理。所以WindowsServer2019里面精简了大量有关图形的功能。办公和打游戏是需要图像处理的...
- 一键还原系统怎么卸载(一键还原系统软件怎么用)
-
打开控制面板,点击“系统”,再点击“高级系统设置”,再点“高级”标签,再点“启动和故障恢复”里的“设置”,,出来新对话框,把“默认操作系统“选到”WIN10,然后下面的”显示操作系统列表的时间“选到0...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
失业程序员复习python笔记——条件与循环
-
系统u盘安装(win11系统u盘安装)
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
