Jenkinsfile的pipeline配置

Posted 浩码农

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jenkinsfile的pipeline配置相关的知识,希望对你有一定的参考价值。

本篇文章目测1700字,实际看完可能还不用6分钟。

我之前的博客《》及《》探索了在多分支构建中Jenkinsfile的配置。然而在我的配置中,根节点为node,但里面却没有使用Jenkinsfile的DSL去声明构建的流程,而是通过groovy代码去实现整个的构建逻辑,用try-catch处理构建失败的问题,看起来很混乱。并且,整个构建本来有拉取并检出代码、构建、发布这几个阶段,但是在Jenkinsfile里却没有体现出来,所以当发现构建较慢时也不利于定位问题所在的步骤。本篇文章将完全处理这些问题。

简单的stage声明

我们还是以之前的代码为例,这里为了专注于具体的配置,我简化一下构建命令及省略掉发邮件的代码,如下:

node {
    checkout scm
    echo "current branch: $BRANCH_NAME"
    try {
       if (BRANCH_NAME.startsWith("release/")) {            sh "./gradlew clean -Ppublish assemble"        } else {            sh "./gradlew clean assembleTest"        }        currentBuild.result = 'SUCCESS'    } catch (any) {        currentBuild.result = 'FAILURE'        throw any    } finally {
       if (currentBuild.result == 'FAILURE') {
           // send e-mail        }    } }

如果说,要把这个流程划分为多个阶段,那还是挺简单的,在node下增加stage节点就可以了,如下:

node {
    stage('Checkout') {
        checkout scm
    }
    stage('Build') {
        echo "current branch: $BRANCH_NAME"
        try {
           if (BRANCH_NAME.startsWith("release/")) {                sh "./gradlew clean -Ppublish assemble"            } else {                sh "./gradlew clean assembleTest"            }            currentBuild.result = 'SUCCESS'        } catch (any) {            currentBuild.result = 'FAILURE'            throw any        } finally {
           if (currentBuild.result == 'FAILURE') {
               // send e-mail            }        }    } }

简单一加,构建一,在Jenkins web页面上就出现了个Stage View了。但是Jenkins会提示我们,这种方式的stage声明已经弃用了。所以接下来介绍一下我所探索的更完整的Jenkinsfile写法。

Pipeline DSL

我们先上一张项目最终的Stage View截图:

这是实际项目的效果,多了加固和热修复相关阶段,本文暂时跳过。接下来,还是使用原来的例子,介绍一下如何实现它。

agent

首先,我们的根节点应声明为pipeline
然后在pipeline下,我们再声明一个agent节点。这个可就厉害了。比如通常我们会有多个构建从节点,但这多个构建节点的配置可能不同,比如有的只配置了android环境用于执行Android项目构建,有的只能执行ios项目构建,有的是用于执行Go项目的。那这么多不同的节点怎么管理及分配呢?那就是通过对节点声明不同的标签label,然后在我们的构建中指定标签,这样Jenkins就会找到有对应标签的节点去执行构建了。假设我们现在用于执行Android项目构建的节点配置了Android标签,那么我们的配置将如下:

pipeline {
    agent {
        label 'Android'
    }
}

options

pipeline内,我们还可以定义一个options,这个可以用于做什么呢?比如我的构建从节点资源有限,所以构建并发数我只定义为2。如果某个构建因为一些网络或其他问题卡住了,build了三天三夜都没停下来,其他要构建的任务(job)就只能排队一个个执行了,如果再来一个构建任务,它又构建了好久没停下来,那么后面的任务就都等不到了。通常遇到这样的情况我们需要手动去取消一下任务,而options里我们可以为整个构建配置一个超时时间。
比如我们的Android项目,通常整个构建都不会超过半个小时,那我们就配置超时时间为半个小时,如果它超时了,就会终止这次的构建。因此,我们的配置修改为如下:

pipeline {
    agent {
        label 'Android'
    }
    options {
        timeout(time: 30, unit: 'MINUTES')
    }
}

options还有其他配置,比如失败后重试整个pipeline的次数:retry(3)。其他的就不一一介绍了,可翻文档。

stages

接下来就是这篇博客的重点之一了。
前面提到,直接添加stage是过时的做法。那么现在未过时的做法是什么呢?那就在在stages里声明。
我们在pipeline下,增加一个stages节点,然后在这个节点里再增加不同的stage。需要注意的是,这里我们已经有默认的检出代码了,所以不需要再写checkout scm。我们将前面的构建步骤,划分为构建和发布。所以我们的Jenkins脚本改为如下:

pipeline {
    agent {
        label 'Android'
    }
    options {
        timeout(time: 30, unit: 'MINUTES')
    }
    stages {
        stage('Build') {
            steps {
                sh './gradlew assembleTest'
            }
        }
        stage('Publish') {
            steps {
                sh './gradlew firTest'
            }
        }
    }
}

注:上面的firTest是我编写的发布到fir.im的插件里的任务。
但是有一点要注意,所有分支都要执行构建,却只有release分支才需要发布到fir上供测试人员下载测试。所以我们需要对Publish阶段增加一个条件,即什么时候下执行,如下:

        stage('Publish') {
            when {
                expression { BRANCH_NAME ==~ /release\/.*/ }
            }
            steps {
                sh './gradlew firTest'
            }
        }

这里用到了groovy的正则匹配的语法,即BRANCH_NANErelease/开头时,才执行下面的步骤。
注意这里的steps,是可以增加多个步骤的。比如构建成功后想要发个消息通知,那就里面增加发个消息通知的命令。

post

上面的stages完成了我们对构建分阶段步骤的需求,但是我们还需要构建失败时能够发邮件通知到我们,这里就用到了post节点了。
我们在pipeline内增加post节点,它定义的是在阶段运行结束时的操作,它支持这样一些后置条件:

  • always 总是运行,无论成功、失败还是其他状态。

  • changed 当前状态与上一次构建状态不同时就运行

  • failure 当前失败时才运行

  • success 当前成功时运行

  • unstable 不稳定状态时运行

  • aborted 被终止时运行。

我们这里需要在失败时进行邮件通知,所以我们添加这样的节点内容:

    post {
        failure {
           // send e-mail        }    }

最终我们的Jenkinsfile如下:

pipeline {
    agent {
        label 'Android'
    }
    options {
        timeout(time: 30, unit: 'MINUTES')
    }
    stages {
        stage('Build') {
            steps {
                sh './gradlew assembleTest'
            }
        }
        stage('Publish') {
            when {
                expression { BRANCH_NAME ==~ /release\/.*/ }
            }
            steps {
                sh './gradlew firTest'
            }
        }
    }
    post {
        failure {
            emailext(
               subject: "Jenkins build is ${currentBuild.result}: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
               mimeType: "text/html",
               body: """<p>Jenkins build is ${currentBuild.result}: ${env.JOB_NAME} #${env.BUILD_NUMBER}:</p>                         <p>Check console output at <a href="${env.BUILD_URL}console">${env.JOB_NAME} #${env.BUILD_NUMBER}</a></p>""",
               recipientProviders: [[$class: 'CulpritsRecipientProvider'],                                     [$class: 'DevelopersRecipientProvider'],                                     [$class: 'RequesterRecipientProvider']]            )        }    } }



关注并后台回复“qq群”,获取Android高级开发群群号。


以上是关于Jenkinsfile的pipeline配置的主要内容,如果未能解决你的问题,请参考以下文章

Jenkins高级用法 - Jenkinsfile 介绍及实战经验

jenkins-使用Jenkinsfile来定义pipeline

jenkinsFile

在 Jenkins Pipeline/Jenkinsfile 中获取 git 分支名称

jenkins集群使用k8s部署,pipeline中文乱码的问题解决记录

持续集成使用 Jenkinsfile 设计直观的 Pipeline