Jenkinsfile的pipeline配置

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/maosidiaoxian/article/details/86569288

我之前的博客《Jenkins 多分支构建》及《Jenkins 多分支构建中的邮件配置》探索了在多分支构建中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截图:
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']]
            )
        }
    }
}

欢迎加入我的知识星球或关注我的公众号。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/maosidiaoxian/article/details/86569288