Jenkins管道可以有一个可选的输入步骤吗?

时间:2018-02-16 07:24:45

标签: jenkins jenkins-pipeline

是否可以使用可选的输入阶段创建Jenkins管道?

以下代码段无法实现此目标。

预期行为

阶段(因此输入提示)应仅针对特定分支运行。

实际行为

此阶段适用于所有分支机构。使用输入步骤时,将忽略when过滤器。

stage('Approve') {
            when {
                expression { BRANCH_NAME ==~ /^qa[\w-_]*$/ }
            }
            input {
                message "Approve release?"
                ok "y"
                submitter "admin"
                parameters {
                    string(name: 'IS_APPROVED', defaultValue: 'y', description: 'Deploy to master?')
                }
            }
            steps {
                script {
                    if (IS_APPROVED != 'y') {
                        currentBuild.result = "ABORTED"
                        error "User cancelled"
                    }
                }
            }
        }

2 个答案:

答案 0 :(得分:6)

不会忽略过滤器,只是在输入步骤后进行评估。在您的示例中,始终会询问您是否要部署,如果您不在QA分支上,则不会发生任何事情。

现在你可以问为什么Jenkins没有先评估'when'指令。在这种情况下,您无法在when条件中使用输入参数。

有多个when指令就像在声明性管道中编写脚本一样。

但是,有一个表达式允许您控制何时评估'when'指令。这是beforeAgent。它允许您在分配代理之前评估when语句。与此类似,你需要像beforeInput这样的东西。你可以create a feature request

我暂时不使用输入指令,现在我在脚本块中使用输入,因为这提供了更大的灵活性,例如当有人必须批准某些事情时,我发送Slack通知,这在声明性方法中是不可能的。你需要一个notify指令。如果有一个,是否会在输入步骤之前或之后进行评估?

你知道,做一切声明并不总是最好的方法。所以我推荐的方法如下(免责声明:这是未经测试的!):

pipeline {
  // We want to use agents per stage to avoid blocking our build agents
  // while we are waiting for user input.
  agent none
  ...
  // The question mark naming convention is helpful to show you which
  //  approval stage belongs to which work stage.
  stage('Release?') {
    // Don't allocate an agent because we don't want to block our
    // slaves while waiting for user input.
    agent none
    when {
      // You forgot the 'env.' in your example above ;)
      expression { env.BRANCH_NAME ==~ /^qa[\w-_]*$/ }
    }
    options {
      // Optionally, let's add a timeout that we don't allow ancient
      // builds to be released.
      timeout time: 14, unit: 'DAYS' 
    }
    steps {
      // Optionally, send some notifications to the approver before
      // asking for input. You can't do that with the input directive
      // without using an extra stage.
      slackSend ...

      // The input statement has to go to a script block because we
      // want to assign the result to an environment variable. As we 
      // want to stay as declarative as possible, we put noting but
      // this into the script block.
      script {
        // Assign the 'DO_RELEASE' environment variable that is going
        //  to be used in the next stage.
        env.DO_RELEASE = input ...
      }
      // In case you approved multiple pipeline runs in parallel, this
      // milestone would kill the older runs and prevent deploying
      // older releases over newer ones.
      milestone 1
    }
  }
  stage('Release') {
    // We need a real agent, because we want to do some real work.
    agent any
    when {
      // Evaluate the 'when' directive before allocating the agent.
      beforeAgent true
      // Only execute the step when the release has been approved.
      environment name: 'DO_RELEASE', value: 'yes'
    }
    steps {
      // Make sure that only one release can happen at a time.
      lock('release') {
        // As using the first milestone only would introduce a race 
        // condition (assume that the older build would enter the 
        // milestone first, but the lock second) and Jenkins does
        // not support inter-stage locks yet, we need a second 
        // milestone to make sure that older builds don't overwrite
        // newer ones.
        milestone 2

        // Now do the actual work here.
        ...
      }
    }
  }

答案 1 :(得分:1)

The correct syntax would be more like (completely untested):

stage('Approve') {
        when {
            expression { BRANCH_NAME ==~ /^qa[\w-_]*$/ }
        }
        steps {
            script {
                def IS_APPROVED = input(
                    message: "Approve release?"
                    ok: "y"
                    submitter: "admin"
                    parameters: [
                        string(name: 'IS_APPROVED', defaultValue: 'y', description: 'Deploy to master?')
                    ]
                )
                if (IS_APPROVED != 'y') {
                    currentBuild.result = "ABORTED"
                    error "User cancelled"
                }
            }
        }
    }

So essentially, you're hitting the limits of declarative pipelines have to fall back to groovy scripting / scripted pipelines.