• Jenkins一站成魔【2】传统项目CI/CD


    Jenkins一站成魔【1】传统安装与说明
    Jenkins一站成魔【2】传统项目CI/CD
    Jenkins一站成魔【3】Docker平台CI/CD
    Jenkins一站成魔【4】K8S平台CI/CD

    1、Jenkins项目构建类型

    1.1、构建的项目类型

    Jenkins常用的自动构建项目的类型有以下三种:
    自由风格软件项目(FreeStyle Project)
    Maven项目(Maven Project)
    流水线项目(Pipeline Project)
    每种类型的构建其实都可以完成一样的构建过程与结果,只是在操作方式、灵活度等方面有所区别,在实际开发中可以根据自己的需求和习惯来选择。(PS:个人推荐使用流水线类型,因为灵活度非常高)

    1.2、自由风格项目构建

    拉取代码 -> 编译 -> 打包 -> 部署

    1.2.1、新建任务

    1.2.2、配置从gitlab拉取代码

    【具体项目x】->【源码管理】->【添加具体操作…】->【应用、保存】
    jenkins拉取代码的默认存储路径:/var/lib/jenkins/workspace/

    1.2.3、编译打包

    【具体项目x】->【构建】->【增加构建步骤】->【Executor Shell】->【添加具体操作…】->【应用、保存】

    echo "开始编译和打包======================>>>>>>>>>>>>"
    mvn clean package
    echo "编译和打包结束======================>>>>>>>>>>>>"
    
    • 1
    • 2
    • 3

    1.2.4、部署项目到远程Tomcat

    • 1)安装 Deploy to container插件
      Jenkins本身无法实现远程部署到Tomcat的功能,需要安装 Deploy to container 插件实现。

    • 2)添加构建后操作及Tomcat用户凭证
      【具体项目x】->【配置】->【构建后操作】->【增加构建后操作步骤】->【Deploy war/ear to a container】->【添加具体操作…】->【应用、保存】

    1.2.5、构建及验证

    【具体项目x】->【立即构建】

    访问验证:http://192.168.168.153:8080/jenkins-web/

    1.2.6、改动代码后的持续集成

    • 1)IDEA中源码修改并提交到gitlab
    • 2)在Jenkins中项目重新构建
    • 3)访问Tomcat

    1.3、Maven项目构建

    1)安装Maven Integration插件
    从此处开始:因插件问题,jenkins 升级为 jenkins-2.361.1-1.1.noarch.rpm 版本!!!
    Maven Integration 插件
    2)创建Maven项目

    3)配置项目
    拉取代码和远程部署的过程和自由风格项目一样,只是 “构建” 部分不同。

    1.4、Pipeline流水线项目构建

    1.4.1、Pipeline简介

    Pipeline,简单说就是一套运行在 Jenkins 上的工作流框架,将原来独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排和可视化的工作。

    使用Pipeline有以下好处(来自翻译自官方文档):
    代码:Pipeline以代码的形式实现,通常被检入源代码控制,使团队能够编辑,审查和迭代其传送流程。
    持久:无论是计划内的还是计划外的服务器重启,Pipeline都是可恢复的。
    可停止:Pipeline可接收交互式输入,以确定是否继续执行Pipeline。
    多功能:Pipeline支持现实世界中复杂的持续交付要求。它支持fork/join、循环执行,并行执行任务的功能。
    可扩展:Pipeline插件支持其DSL的自定义扩展 ,以及与其他插件集成的多个选项。

    如何创建 Jenkins Pipeline呢?
    Pipeline 脚本是由 Groovy 语言实现的,但没必要单独去学习 Groovy语言。
    Pipeline 支持两种语法:Declarative(声明式)和 Scripted Pipeline(脚本式)语法
    Pipeline 也有两种创建方法:可以直接在 Jenkins 的 Web UI 界面中输入脚本;也可以通过创建一个 Jenkinsfile 脚本文件放入项目源码库中(一般推荐在 Jenkins 中直接从源代码控制(SCM)中直接载入 Jenkinsfile Pipeline 这种方法)。

    1.4.2、安装Pipeline插件

    【系统管理】->【插件管理】->【可选插件】Pipeline
    A suite of plugins that lets you orchestrate automation, simple or complex. See Pipeline as Code with Jenkins for more details.

    安装插件后,创建任务的时候多了“流水线”类型的任务。

    1.4.3、Pipeline语法快速入门

    1.4.3.1、Declarative声明式-Pipeline

    创建项目 -> 流水线 ->选择 Pipeline script 选择 Hello World 模板生成内容如下:

    pipeline {
        agent any
        stages {
            stage('Hello') {
                steps {
                    echo 'Hello World'
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    stages:代表整个流水线的所有执行阶段。通常stages只有1个,里面包含多个stage。
    stage:代表流水线中的某个阶段,可能出现n个。一般分为拉取代码,编译构建,部署等阶段。
    steps:代表一个阶段内需要执行的逻辑。steps里面是shell脚本,git拉取代码,ssh远程发布等任意内容。

    编写一个简单声明式Pipeline:

    pipeline {
        agent any
        stages {
            stage('拉取代码') {
                steps {
                    echo '拉取代码呀!!!'
                }
            }
            stage('编译构建') {
                steps {
                    echo '编译构建呀!!!'
                }
            }
            stage('项目部署') {
                steps {
                    echo '项目部署呀!!!'
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    点击 “立即构建”,可以看到整个构建过程。

    1.4.3.2、Scripted Pipeline脚本式-Pipeline

    创建项目 -> 流水线 ->选择 Pipeline script -> 选择 Scripted Pipeline 模板生成内容如下:

    node {
        def mvnHome
        stage('Preparation') { // for display purposes
            // Get some code from a GitHub repository
            git 'https://github.com/jglick/simple-maven-project-with-tests.git'
            // Get the Maven tool.
            // ** NOTE: This 'M3' Maven tool must be configured
            // **       in the global configuration.
            mvnHome = tool 'M3'
        }
        stage('Build') {
            // Run the maven build
            withEnv(["MVN_HOME=$mvnHome"]) {
                if (isUnix()) {
                    sh '"$MVN_HOME/bin/mvn" -Dmaven.test.failure.ignore clean package'
                } else {
                    bat(/"%MVN_HOME%\bin\mvn" -Dmaven.test.failure.ignore clean package/)
                }
            }
        }
        stage('Results') {
            junit '**/target/surefire-reports/TEST-*.xml'
            archiveArtifacts 'target/*.jar'
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    node:节点,一个 Node 就是一个 Jenkins 节点,Master 或者 Agent,是执行 Step 的具体运行环境,后续讲到Jenkins的Master-Slave架构的时候用到。
    stage:阶段,一个 Pipeline 可以划分为若干个 Stage,每个 Stage 代表一组操作,比如:Build、Test、Deploy,Stage 是一个逻辑分组的概念。
    step:步骤,Step 是最基本的操作单元,可以是打印一句话,也可以是构建一个 Docker 镜像,由各类 Jenkins 插件提供,比如命令:sh ‘make’,就相当于我们平时 shell 终端中执行 make 命令一样。

    编写一个简单的脚本式Pipeline

    node {
        def mvnHome
        stage('拉取代码') { // for display purposes
            echo '拉取代码呀!!!!'
        }
        stage('编译构建') {
            echo '编译构建呀!!!!'
        }
        stage('项目部署') {
            echo '项目部署呀!!!!'
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    构建结果和声明式一样!

    1.4.4、Pipeline拉取代码

    选择 【片段生成器】->【checkout: Check out from version control】

    pipeline {
      agent any
      stages {
        stage('拉取代码') {
          steps {
    		checkout([$class: 'GitSCM', 
    				  branches: [[name: '*/master']], 
    				  extensions: [], 
    				  userRemoteConfigs: [[credentialsId: '0ea1a070-8bae-439f-b81a-1aeffc6245e1', 
    									   url: 'http://192.168.168.151:8000/test-jenkins/jenkins-web.git']]])
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    1.4.5、Pipeline编译打包

    选择 【片段生成器】->【sh: Shell Script】

    pipeline {
      agent any
      stages {
        stage('拉取代码') {
          steps {
    		checkout([$class: 'GitSCM', 
    				  branches: [[name: '*/master']], 
    				  extensions: [], 
    				  userRemoteConfigs: [[credentialsId: '0ea1a070-8bae-439f-b81a-1aeffc6245e1', 
    									   url: 'http://192.168.168.151:8000/test-jenkins/jenkins-web.git']]])
          }
        }
        stage('编译构建') {
          steps {
            sh 'mvn clean package'
            echo "这一行这样写 也行"
            echo "sh label: '', script: 'mvn clean package'"
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.4.6、Pipeline部署项目

    选择 【片段生成器】->【deploy: Delpoy war/ear to a container】

    pipeline {
      agent any
      stages {
        stage('拉取代码') {
          steps {
    		checkout([$class: 'GitSCM', 
    				  branches: [[name: '*/master']], 
    				  extensions: [], 
    				  userRemoteConfigs: [[credentialsId: '0ea1a070-8bae-439f-b81a-1aeffc6245e1', 
    									   url: 'http://192.168.168.151:8000/test-jenkins/jenkins-web.git']]])
          }
        }
        stage('编译构建') {
          steps {
            sh 'mvn clean package'
            echo "这一行这样写 也行"
            echo "sh label: '', script: 'mvn clean package'"
          }
        }
        stage('项目部署') {
          steps {
            deploy adapters: [tomcat8(credentialsId: '51df8b8a-5928-4fc5-8f68-ffd250d30f53', 
            						  path: '', 
            						  url: 'http://192.168.168.153:8080')], 
            	   contextPath: null, 
            	   war: 'target/*.war'
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    1.4.7、Pipeline Script from SCM

    刚才是直接在Jenkins的UI界面编写Pipeline代码,这样不方便脚本维护,建议把Pipeline脚本放在项目中(一起进行版本控制)。
    1)在项目根目录建立Jenkinsfile文件,把Pipeline代码内容复制到该文件中,并将Jenkinsfile上传到Gitlab。

    2)在Jenkins任务中引用该文件

    2、Jenkins项目构建细节

    2.1、Jenkins内置4种构建触发器

    远程触发构建
    其他工程构建后触发(Build after other projects are build)
    定时构建(Build periodically)
    轮询SCM(Poll SCM)

    2.1.1、远程触发构建

    在这里插入图片描述

    JENKINS_URL/job/pipeline-jenkins-web/build?token=TOKEN_NAME
    JENKINS_URL/job/pipeline-jenkins-web/buildWithParameters?token=TOKEN_NAME
    注意:此操作需要登录系统,我感觉挺鸡肋的…

    2.1.2、其他工程构建后触发(Build after other projects are build)

    在这里插入图片描述
    构建指定的任务 后,自动 构建目标任务

    2.1.3、定时构建(Build periodically)

    定时字符串规则从左往右分别为: 分 时 日 月 周

    H/2 * * * *          每2分钟构建一次:H代表形参 8:02 8:04
    H H/2 * * *          每2个小时构建一次: 
    0 8,12,22 * * *      每天的8点,12点,22点,一天构建3次: (多个时间点中间用逗号隔开) 
    H 12 * * *           每天中午12点定时构建一次 
    H 18 * * *           每天下午18点定时构建一次 
    H(0-29)/10 * * * *   在每个小时的前半个小时内的每10分钟 
    H H(9-16)/2 * * 1-5  每两小时一次,每个工作日上午9点到下午5点(也许是上午10:38,下午12:38,下午2:38,下午4:38) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    2.1.4、轮询SCM(Poll SCM)

    轮询SCM,是定时扫描本地代码仓库的代码是否有变更,如果代码有变更就触发项目构建。
    在这里插入图片描述

    注意:Jenkins会定时扫描本地整个项目的代码,增大系统的开销,不建议使用。

    2.2、Git hook自动触发构建

    轮询SCM可以实现Gitlab代码更新,项目自动构建,但该方案性能不佳。
    我们可以利用Gitlab的webhook实现代码push到仓库,立即触发项目自动构建。

    2.2.1、安装 Generic Webhook TriggerGitLab 插件

    2.2.2、Jenkins设置自动构建

    在这里插入图片描述

    【Gitlab】-> 【关闭 Enable authentication for ‘/project’ end-point】

    在这里插入图片描述

    2.2.3、Gitlab设置Webhook

    【Admin Area】->【Settings】->【Network】->【Outbound requests】->【开启Allow requests to the local network from web hooks and services】->【Save changes】
    在这里插入图片描述

    【具体项目xxx】->【Settings】->【Integrations】->【URL:填写Jenkins生成的url】->【Add webhook】
    在这里插入图片描述

    2.3、Jenkins的参数化构建

    在这里插入图片描述
    选择文本参数
    project_branch
    master
    在这里插入图片描述

    bran

    修改脚本 使用刚才配置的参数,提交

    pipeline {
      agent any
      stages {
        stage('拉取代码') {
          steps {
    		checkout([$class: 'GitSCM', 
    				  branches: [[name: '*/${project_branch}']], 
    				  extensions: [], 
    				  userRemoteConfigs: [[credentialsId: '0ea1a070-8bae-439f-b81a-1aeffc6245e1', 
    									   url: 'http://192.168.168.151:8000/test-jenkins/jenkins-web.git']]])
          }
        }
        stage('编译构建') {
          steps {
            sh 'mvn clean package'
            echo "这一行这样写 也行"
            echo "sh label: '', script: 'mvn clean package'"
          }
        }
        stage('项目部署') {
          steps {
            deploy adapters: [tomcat8(credentialsId: '51df8b8a-5928-4fc5-8f68-ffd250d30f53', 
            						  path: '', 
            						  url: 'http://192.168.168.153:8080')], 
            	   contextPath: null, 
            	   war: 'target/*.war'
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    2.4、配置邮箱服务器发送构建结果

    2.4.1、安装 Email Extension Template 插件

    准备邮件内容

    2.4.2、Jenkins设置邮箱相关参数

    以163邮箱为例:
    首先 邮箱要开启 POP3/SMTP/IMAP

    在这里插入图片描述
    开启之后,会生成一个 验证字符串,保存好 作为jenkins使用。
    SMTP服务器: smtp.163.com
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    Content Token Reference中有很多系统变量 可以使用

    pipeline {
      agent any
      stages {
        stage('拉取代码') {
          steps {
    		checkout([$class: 'GitSCM', 
    				  branches: [[name: '*/${project_branch}']], 
    				  extensions: [], 
    				  userRemoteConfigs: [[credentialsId: '0ea1a070-8bae-439f-b81a-1aeffc6245e1', 
    									   url: 'http://192.168.168.151:8000/test-jenkins/jenkins-web.git']]])
          }
        }
        stage('编译构建') {
          steps {
            sh 'mvn clean package'
            echo "这一行这样写 也行"
            echo "sh label: '', script: 'mvn clean package'"
          }
        }
        stage('项目部署') {
          steps {
            deploy adapters: [tomcat8(credentialsId: '51df8b8a-5928-4fc5-8f68-ffd250d30f53', 
            						  path: '', 
            						  url: 'http://192.168.168.153:8080')], 
            	   contextPath: null, 
            	   war: 'target/*.war'
          }
        }
      }
      post {
        always {
          emailext(
            subject: '构建通知:${PROJECT_NAME} - Build # ${BUILD_NUMBER} - ${BUILD_STATUS}!',
            body: '${FILE,path="email.html"}',
            to: 'michael_linux@163.com'
          )
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    3、Jenkins+SonarQube代码审查

    SonarQube是一个用于管理代码质量的开放平台,可以快速的定位代码中潜在的或者明显的错误。目前支持java,C#,C/C++,Python,PL/SQL,Cobol,JavaScrip,Groovy等二十几种编程语言的代码质量管理与检测。
    官网:https://www.sonarqube.org/

    3.1、安装SonarQube

    软件服务器版本
    JDK192.168.168.1521.8
    MySQL192.168.168.1525.7
    SonarQube192.168.168.1526.7.4

    1)安装MySQL(已完成)
    在MySQL创建 sonar 数据库。

    2)安装SonarQube

    下载sonar压缩包:https://www.sonarqube.org/downloads/

    yum install -y unzip
    unzip sonarqube-6.7.4.zip
    mkdir -p /opt/sonar
    mv sonarqube-6.7.4/* /opt/sonar
    useradd sonar
    chown -R sonar. /opt/sonar
    
    修改sonar配置文件
    vim /opt/sonar/conf/sonar.properties
    内容如下:
    sonar.jdbc.username=root
    sonar.jdbc.password=Csdn@123456
    sonar.jdbc.url=jdbc:mysql://192.168.168.101:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false
    注意:sonar默认监听9000端口,如果9000端口被占用,需要更改。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    启动sonar

    // 启动
    su - sonar /opt/sonar/bin/linux-x86-64/sonar.sh start
    // 查看状态
    su - sonar /opt/sonar/bin/linux-x86-64/sonar.sh status
    // 停止
    su - sonar /opt/sonar/bin/linux-x86-64/sonar.sh stop
    // 查看日志
    tail -f /opt/sonar/logs/sonar.logs
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    访问sonar:http://192.168.168.152:9000
    在这里插入图片描述
    默认账户:admin/admin

    创建token:
    在这里插入图片描述
    token要记下来后面要使用:
    admin: 86f80281522f67f75495e1b6afcfb4399a25af5f

    安装Jenkins的 SonarQube Scanner 插件
    在这里插入图片描述

    安装Jenkins连接SonarQube的 SonarQube Scanner 工具
    方法1、可以在linux直接安装。
    方法2、使用jenkins页面直接安装。

    添加SonarQube凭证
    在这里插入图片描述

    Jenkins进行SonarQube配置
    Manage Jenkins->Configure System->SonarQube servers
    在这里插入图片描述

    系统管理->Global Tool Configuration->SonarQube Scanner

    在这里插入图片描述

    SonaQube关闭审查结果上传到SCM功能
    在这里插入图片描述

    3.2、实现代码审查

    在这里插入图片描述

    构建->增加构建步骤(选择:Execute SonarQube Scanner
    在这里插入图片描述

    3.2.1、在项目添加SonaQube代码审查(非流水线项目)

    使用 Analysis properties 配置扫描规则。
    添加构建步骤:

    # must be unique in a given SonarQube instance
    sonar.projectKey=web_demo123
    # this is the name and version displayed in the SonarQube UI. Was mandatory
    prior to SonarQube 6.1.
    sonar.projectName=web_demo123
    sonar.projectVersion=1.0
    # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
    # This property is optional if sonar.modules is set.
    sonar.sources=.
    sonar.exclusions=**/test/**,**/target/**
    sonar.java.source=1.8
    sonar.java.target=1.8
    # Encoding of the source code. Default is default system encoding
    sonar.sourceEncoding=UTF-8
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    构建日志
    在这里插入图片描述
    查看构建扫描结果
    http://192.168.168.152:9000/dashboard?id=web_demo123
    在这里插入图片描述

    3.2.2、在项目添加SonaQube代码审查(流水线项目)

    使用 Path to project properties 配置扫描规则。
    1)项目根目录下,创建sonar-project.properties文件

    # must be unique in a given SonarQube instance
    sonar.projectKey=web_demo_pipline
    # this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1.
    sonar.projectName=web_demo_pipline
    sonar.projectVersion=1.0
    
    # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
    # This property is optional if sonar.modules is set.
    sonar.sources=.
    sonar.exclusions=**/test/**,**/target/**
    
    # target of jdk
    sonar.java.source=1.8
    sonar.java.target=1.8
    # Encoding of the source code. Default is default system encoding
    sonar.sourceEncoding=UTF-8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2)修改Jenkinsfile,加入SonarQube代码审查阶段

    pipeline {
    	agent any
    	stages {
    		stage('拉取代码') {
    			steps {
    				checkout([$class: 'GitSCM', branches: [[name: '*/master']],
    				doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [],
    				userRemoteConfigs: [[credentialsId: '68f2087f-a034-4d39-a9ff-1f776dd3dfa8', url:
    				'git@192.168.66.100:itheima_group/web_demo.git']]])
    			}
    		}
    		
    		stage('编译构建') {
    			steps {
    				sh label: '', script: 'mvn clean package'
    			}
    		}
    		
    		stage('SonarQube代码审查') {
    			steps{
    				script {
    					// 全局工具配置:SonarQube Scanner 你配置的名字
    					scannerHome = tool 'SonarQube-Scanner'
    				}
    				// 系统配置:SonarQube servers 你配置的名字
    				withSonarQubeEnv('sonarqube') {
    					sh "${scannerHome}/bin/sonar-scanner"
    				}
    			}
    		}
    		
    		stage('项目部署') {
    			steps {
    				deploy adapters: [tomcat8(credentialsId: 'afc43e5e-4a4e-4de6-984fb1d5a254e434',
    				path: '', url: 'http://192.168.168.153:8080')], contextPath: null,
    				war: 'target/*.war'
    			}
    		}
    	}
    	post {
    		always {
    			emailext(
    				subject: '构建通知:${PROJECT_NAME} - Build # ${BUILD_NUMBER} - ${BUILD_STATUS}!',
    				body: '${FILE,path="email.html"}',
    				to: 'michael_linux@163.com'
    			)
    		}
    	}
    }
    
    ```
    
    
    3)到SonarQube的UI界面查看审查结果
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/778dab28bdb64476ba2a162a0ead6f4c.png)
    
    
    
    
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
  • 相关阅读:
    【Pandas数据处理100例】(八十七):Pandas使用get_dummies构建哑变量
    线性代数-3
    细胞膜修饰脂质体/蛋白/聚合物纳米载体/纳米胶束/磁性纳米粒子
    机器学习系列:LightGBM 可视化调参
    Kubernetes中Pod容器的使用
    教师节到了,用Python做了个非常好用的学生点名系统
    消防建筑防火3d实景漫游展示方案及特点
    华为机试 - 高矮个子排队
    代码随想录算法训练营第23期day53|1143.最长公共子序列、1035.不相交的线、53. 最大子序和
    腾讯云便宜服务器有哪些?腾讯云这个服务器一个月7块钱!
  • 原文地址:https://blog.csdn.net/Michael_lcf/article/details/127778800