百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Jenkins Pipeline+SonarQube+Python集成钉钉自动通知(webhook版)

off999 2024-10-11 14:04 101 浏览 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 json262

5.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中的实践

相关推荐

安全教育登录入口平台(安全教育登录入口平台官网)

122交通安全教育怎么登录:122交通网的注册方法是首先登录网址http://www.122.cn/,接着打开网页后,点击右上角的“个人登录”;其次进入邮箱注册,然后进入到注册页面,输入相关信息即可完...

大鱼吃小鱼经典版(大鱼吃小鱼经典版(经典版)官方版)

大鱼吃小鱼小鱼吃虾是于谦跟郭麒麟的《我的棒儿呢?》郭德纲说于思洋郭麒麟作诗的相声,最后郭麒麟做了一首,师傅躺在师母身上大鱼吃小鱼小鱼吃虾虾吃水水落石出师傅压师娘师娘压床床压地地动山摇。...

谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
哪个软件可以免费pdf转ppt(免费的pdf转ppt软件哪个好)
哪个软件可以免费pdf转ppt(免费的pdf转ppt软件哪个好)

要想将ppt免费转换为pdf的话,我们建议大家可以下一个那个wps,如果你是会员的话,可以注册为会员,这样的话,在wps里面的话,就可以免费将ppt呢转换为pdfpdf之后呢,我们就可以直接使用,不需要去直接不需要去另外保存,为什么格式转...

2026-02-04 09:03 off999

电信宽带测速官网入口(电信宽带测速官网入口app)

这个网站看看http://www.swok.cn/pcindex.jsp1.登录中国电信网上营业厅,宽带光纤,贴心服务,宽带测速2.下载第三方软件,如360等。进行在线测速进行宽带测速时,尽...

植物大战僵尸95版手机下载(植物大战僵尸95 版下载)

1可以在应用商店或者游戏平台上下载植物大战僵尸95版手机游戏。2下载教程:打开应用商店或者游戏平台,搜索“植物大战僵尸95版”,找到游戏后点击下载按钮,等待下载完成即可安装并开始游戏。3注意:确...

免费下载ppt成品的网站(ppt成品免费下载的网站有哪些)

1、Chuangkit(chuangkit.com)直达地址:chuangkit.com2、Woodo幻灯片(woodo.cn)直达链接:woodo.cn3、OfficePlus(officeplu...

2025世界杯赛程表(2025世界杯在哪个国家)

2022年卡塔尔世界杯赛程公布,全部比赛在卡塔尔境内8座球场举行,2022年,决赛阶段球队全部确定。揭幕战于当地时间11月20日19时进行,由东道主卡塔尔对阵厄瓜多尔,决赛于当地时间12月18日...

下载搜狐视频电视剧(搜狐电视剧下载安装)

搜狐视频APP下载好的视频想要导出到手机相册里方法如下1、打开手机搜狐视频软件,进入搜狐视频后我们点击右上角的“查找”,找到自已喜欢的视频。2、在“浏览器页面搜索”窗口中,输入要下载的视频的名称,然后...

pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
永久免费听歌网站(丫丫音乐网)

可以到《我爱音乐网》《好听音乐网》《一听音乐网》《YYMP3音乐网》还可以到《九天音乐网》永久免费听歌软件有酷狗音乐和天猫精灵,以前要跳舞经常要下载舞曲,我从QQ上找不到舞曲下载就从酷狗音乐上找,大多...

音乐格式转换mp3软件(音乐格式转换器免费版)

有两种方法:方法一在手机上操作:1、进入手机中的文件管理。2、在其中选择“音乐”,将显示出手机中的全部音乐。3、点击“全选”,选中所有音乐文件。4、点击屏幕右下方的省略号图标,在弹出菜单中选择“...

电子书txt下载(免费的最全的小说阅读器)

1.Z-library里面收录了近千万本电子书籍,需求量大。2.苦瓜书盘没有广告,不需要账号注册,使用起来非常简单,直接搜索预览下载即可。3.鸠摩搜书整体风格简洁清晰,书籍资源丰富。4.亚马逊图书书籍...

最好免费观看高清电影(播放免费的最好看的电影)

在目前的网上选择中,IMDb(互联网电影数据库)被认为是最全的电影网站之一。这个网站提供了各种类型的电影和电视节目的海量信息,包括剧情介绍、演员表、评价、评论等。其还提供了有关电影制作背后的详细信息,...

孤单枪手2简体中文版(孤单枪手2简体中文版官方下载)

要将《孤胆枪手2》游戏的征兵秘籍切换为中文,您可以按照以下步骤进行操作:首先,打开游戏设置选项,通常可以在游戏主菜单或游戏内部找到。然后,寻找语言选项或界面选项,点击进入。在语言选项中,选择中文作为游...

取消回复欢迎 发表评论: