如何在Jenkins multibranch管道中调度具有特定参数的作业

时间:2018-06-15 06:52:23

标签: jenkins jenkins-pipeline multibranch-pipeline

我们在Jenkins上有2个FreeStyle项目:

一个用于生成构建(每日构建+手动构建),另一个用于执行测试。

我们正在转向jenkins上的Multibranch管道,所以我的理解是每个存储库有一个项目,我们应该使用选项来实现不同的行为。

所以我可以创建参数,以指示我们是否要运行测试,如果我们想要构建设置,那部分我就可以了。

我的问题是我需要默认情况下,测试不会被执行(因为它们需要花费大量时间来生成,我不希望开发人员错误地只检查“执行测试”选项。

我需要在夜间执行每日构建时检查此选项。

所以有2个问题:

  1. 如何安排?
  2. 如何提供此计划使用的参数值?

4 个答案:

答案 0 :(得分:4)

您可以创建一个单独的多分支作业,该作业将按计划运行,并触发覆盖所有必要参数的主作业。 看起来像这样

pipeline {
    agent any
    triggers {
        pollSCM('0 0 * * *')
    }
    stages {
        stage('Triggering the main job') {
            steps {
                build job: "main/${BRANCH_NAME.replace('/', '%2F')}", 
                      parameters: [string(name: 'RUN_TESTS', value: 'true')]
            }
        }
    }
}

您应将此文件与主Jenkinsfile一起放入存储库,并配置单独的多分支管道作业以使用此文件。

答案 1 :(得分:4)

要将其保留在同一工作中,将需要一些常规代码。由于您使用MultiBranch管道,因此这一切都可以存在于您的Jenkinsfile

首先,如Vitalii所述设置您的cron,这将按计划开始工作。

properties([
    pipelineTriggers([cron('0 0 * * *')])
])

接下来,当计划触发该作业时,我们要调整其运行的参数。因此,首先我们需要检查是什么导致了构建。这可能需要安全脚本的批准。

List causes = currentBuild.rawBuild.getCauses().collect { it.getClass().getCanonicalName().tokenize('.').last() }

如果其中包含'TimerTriggerCause',那么我们要更新参数。

if (causes.contains('TimerTriggerCause') { 
    setBooleanParam("EXECUTE_TESTS", true)
}

为此,我们在共享库中编写了一个函数,您可以根据需要将其放在相同的Jenkinsfile中(在管道逻辑的底部):

/**
 * Change boolean param value during build
 *
 * @param paramName new or existing param name
 * @param paramValue param value
 * @return nothing
 */
Void setBooleanParam(String paramName, Boolean paramValue) {
    List<ParameterValue> newParams = new ArrayList<>();
    newParams.add(new BooleanParameterValue(paramName, paramValue))
    try {
        $build().addOrReplaceAction($build().getAction(ParametersAction.class).createUpdated(newParams))
    } catch (err) {
        $build().addOrReplaceAction(new ParametersAction(newParams))
    }
}

然后让工作照常进行。在评估params.EXECUTE_TESTS时,它现在为true(而不是默认的false)。

注意:可能需要为该值导入模型

import hudson.model.BooleanParameterValue

将所有内容放在一起(只是将这些位快速拼凑在一起就可以看到整体图像),您的jenkinsfile最终会像这样

#!groovy
import hudson.model.BooleanParameterValue


List paramsList = [
    choice(name: 'ACCOUNT_NAME', choices: ['account1', 'account2'].join('\n'), description: 'A choice param'),
    string(name: 'PARAM', defaultValue: 'something', description: 'A string param'),
    booleanParam(defaultValue: false, name: 'EXECUTE_TESTS', description: 'Checkbox'),
]

properties([
    buildDiscarder(logRotator(numToKeepStr: '20')),
    pipelineTriggers([cron('0 18 * * *')]), // 4am AEST/5am AEDT
    disableConcurrentBuilds(),
    parameters(paramList)
])


ansiColor {
    timestamps {
        node {
            try {
                causes = currentBuild.rawBuild.getCauses().collect { it.getClass().getCanonicalName().tokenize('.').last() }
                if (causes.contains('TimerTriggerCause') { 
                    setBooleanParam("EXECUTE_TESTS", true)
                }
                stage('Do the thing') {
                    // Normal do the things like build
                }
                stage('Execute tests if selected') {
                    if (params.EXECUTE_TESTS == true) {
                        // execute tests
                    } else {
                        echo('Tests not executed (Option was not selected/False)')
                    }
                }
            } catch (err) {
                throw err
            } finally {
                deleteDir()
            }
        }
    }
}

/**
 * Change boolean param value during build
 *
 * @param paramName new or existing param name
 * @param paramValue param value
 * @return nothing
 */
Void setBooleanParam(String paramName, Boolean paramValue) {
    List<ParameterValue> newParams = new ArrayList<>();
    newParams.add(new BooleanParameterValue(paramName, paramValue))
    try {
        $build().addOrReplaceAction($build().getAction(ParametersAction.class).createUpdated(newParams))
    } catch (err) {
        $build().addOrReplaceAction(new ParametersAction(newParams))
    }
}

答案 2 :(得分:0)

允许我提出一个更简单的方法。这是用于声明式管道。

我建议将测试(与应用程序代码一起作为第一类代码)和应用程序源代码保存在同一存储库中。

当Jenkins从您的SCM中签出时,将它们放在一起放在一个存储库中,将允许您在测试套件通过时应用标签(标签)。标签是您最好的朋友,应尽可能成功地使用标签。不幸的是,撰写本文时,多SCM结帐似乎不支持标签的应用。

我目前有一个由5个开发人员组成的团队,并且目前正在使用多分支管道来处理他们正在生成的所有功能分支。我们也有一个“主”和“集成”分支。主人是干净的发布。集成是我的关键分支。

在声明式管道中进行调度非常简单:

triggers { 
    cron('0 22 * * *') 
}

对于更复杂的crontab,pollSCM似乎无法可靠地工作。

您可能要考虑在声明性管道中引入条件性的一种方法是通过分支名称。

success {
    script {
        if( "${env.BRANCH_NAME}" == "integration" ) {
           //Create Package
           //Upload to Artifactory
           //Apply tag to Git
        }
    }
}

以上示例中的功能分支仅执行单元测试并将反馈提供给开发人员。只有集成分支才能成功生成工件(用于以后的测试阶段)并标记存储库。

如果您不希望基于分支的分支行为,我建议您在Jenkins中定义2个不同的工作(管道):一个工作在白天运行开发人员的每个提交,另一个计划在晚上运行执行长时间运行的测试。

这就是我对单元测试和系统测试所做的。

单元测试作业是一个多分支管道,在Enterprise GitHub存储库中的每个分支上运行。每分钟轮询一次更改,仅由Integration分支创建工件和标记。单元测试需要10分钟才能运行。

系统测试作业是一个简单的管道,计划在每晚运行,执行需要一个小时左右。

答案 3 :(得分:0)

这在多分支管道中的映射不太好。我已经看到阶段取决于分支,但没有阶段取决于参数-它也会每天破坏阶段视图。

我建议写两个单独的Jenkinsfiles,例如一个叫做Jenkinsfile,另一个也许叫Jenkinsnightlyfile。 然后,您可以在同一存储库中使用不同的jenkinsfile名称创建两个多分支管道项目。

将常规阶段放在第一个阶段,将所有测试置于另一个阶段(为清楚起见,您也可以将这里的工作划分为多个阶段),请确保另一个阶段使用适当的触发器,例如jenkins-管道:

properties([
    pipelineTriggers([cron('0 0 * * *')])
])

或声明性管道:

triggers { 
    cron('0 0 * * *') 
}