将参数从 Jenkinsfile 传递到共享库

Kal*_*ist 5 jenkins jenkins-pipeline

我有几个组件(带有自己的 Bitbucket 存储库的代码项目),每个组件都有一个 Jenkinsfile,如下所示:

properties([parameters([string(defaultValue: "", description: "List of components", name: 'componentsToUpdate'),
                        string(defaultValue: "refs%2Fheads%2Fproject%2Fintegration", description: "BuildInfo CommitID", name: 'commitId'),
                        string(defaultValue: "", description: "Tag to release, e.g. 1.1.0-integration", name: 'releaseTag'),
                        string(defaultValue: "", description: "Forked buildInfo repo. Be aware right commit ID!!!", name: 'fork')]),
                        [$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '7', numToKeepStr: '5']],
                        disableConcurrentBuilds()])

@Library('jenkins-shared-stages')

import mergePipeline
import releasePipeline
import ripplePipeline
import componentPipeline


def branchName = env.BRANCH_NAME
def rewriteDependencies = ""
def returnValue = null
def forkedRepo = params.fork
def buildInfoCommitId = params.commitId
def tagToRelease = params.releaseTag
println "buildInfoCommitId: " + buildInfoCommitId
if(params.componentsToUpdate) {
    rewriteDependencies = params.componentsToUpdate
}

if (branchName == "project/integration") {
    mergePipeline {
    }
} else if (branchName == 'master') {
    releasePipeline {
        releaseTag = tagToRelease
    }
} else {
    returnValue = componentPipeline {
        componentsToUpdate = rewriteDependencies
        commitId = buildInfoCommitId
        runOnForkedRepo = forkedRepo
    }

    rewriteDependencies = rewriteDependencies.isEmpty() ? returnValue : rewriteDependencies + "," + returnValue
    println "WHAT is rewriteDependencies? " + rewriteDependencies
    println "The return value: " + returnValue
    ripplePipeline {
        commitId = buildInfoCommitId
        componentName = returnValue
        runOnForkedRepo = forkedRepo
        componentsToUpdate = rewriteDependencies
    }
}
Run Code Online (Sandbox Code Playgroud)

需要使用“包装器”管道,例如 wrapperPipeline.groovy:

import mergePipeline
import releasePipeline
import ripplePipeline
import componentPipeline
import org.slf4j.Logger
import org.slf4j.LoggerFactory

def call(body) {

    final Logger logger = LoggerFactory.getLogger(wrapperPipeline)

    def config = [:]
    body.resolveStrategy = Closure.DELEGATE_FIRST
    body.delegate = config
    body()

    // Assuming we have multibranch pipeline job or defined branch name in the env
    def branchName = env.BRANCH_NAME
    // There is a bug in the Jenkins it will pass a string "null" as a gradle build parameter instead of NULL object if there is
    // empty parameter has been passed!!!
    def rewriteDependencies = ""
    def returnValue = null
    def forkedRepo = config.runOnForkedRepo
    def buildInfoCommitId = config.commitId
    def tagToRelease = config.releaseTag

    def globalVars = new se.GlobalVars()
    def notifyHandler = new se.NotifyHandler()

    node(globalVars.getAgent('buildAgent')) {
        def PIPELINE_NAME = "wrapperPipeline"

        try {
            logger.info("The buildInfoCommitId is {}", buildInfoCommitId)
            logger.info("Branch name: {}", branchName)

            println "buildInfoCommitId:  "+buildInfoCommitId
            println"Branch name: "+branchName

            if (config.componentsToUpdate) {
                rewriteDependencies = config.componentsToUpdate
            }

    // keep the same integration pipeline for the master branch for now
            if (branchName == "project/integration") {
                logger.info("Invoking mergePipeline")
                println "Invoking mergePipeline"
                mergePipeline {
                }
            } else if (branchName == 'master') {
                logger.info("Invoking releasePipeline")
                println "Invoking releasePipeline"
                releasePipeline {
                    releaseTag = tagToRelease
                }
            } else {
                logger.info("Invoking componentPipeline")
                println "Invoking componentPipeline"

                returnValue = componentPipeline {
                    componentsToUpdate = rewriteDependencies
                    commitId = buildInfoCommitId
                    runOnForkedRepo = forkedRepo
                }
                logger.info("Component pipeline has returned {}", returnValue)
                println "Component pipeline has returned"+returnValue

                // We need to provide new version of the component to the Ripple builds
                rewriteDependencies = rewriteDependencies.isEmpty() ? returnValue : rewriteDependencies + "," + returnValue
                logger.info("rewriteDependencies: {}", rewriteDependencies)
                println "The return value: " + returnValue
                ripplePipeline {
                    commitId = buildInfoCommitId
                    componentName = returnValue
                    runOnForkedRepo = forkedRepo
                    componentsToUpdate = rewriteDependencies
                }
            }
        }
        catch (err) {
            def build_status = "Exception ${err.message} in build ${env.BUILD_ID}"
            logger.error(build_status,err)
            notifyHandler.NotifyFail(build_status, PIPELINE_NAME)

            throw err
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

修改后的 Jenkinsfile:

properties([parameters([string(defaultValue: "", description: "List of components", name: 'componentsToUpdate'),
                        string(defaultValue: "refs%2Fheads%2Fproject%2Fintegration", description: "BuildInfo CommitID", name: 'commitId'),
                        string(defaultValue: "", description: "Tag to release, e.g. 1.1.0-integration", name: 'releaseTag'),
                        string(defaultValue: "", description: "Forked buildInfo repo. Be aware right commit ID!!!", name: 'fork')]),
                        [$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '7', numToKeepStr: '5']],
                        disableConcurrentBuilds()])

@Library('jenkins-shared-stages@integration/CICD-959-wrapper-pipeline-for-the-jenkinsfile') _

import wrapperPipeline

wrapperPipeline{}
Run Code Online (Sandbox Code Playgroud)

现在,我怀疑params对象(来自 Jenkinsfile 的属性)没有正确填充。例如

def buildInfoCommitId = config.commitId
.
.
.
println "buildInfoCommitId:  "+buildInfoCommitId
Run Code Online (Sandbox Code Playgroud)

打印为空。

如何正确调用 wrapperPipeline?

注意:我是 Jenkins 管道和 Groovy 的新手 :)

met*_*ain 5

因为这些是 Jenkins 参数,所以它们不在配置对象中。

您将访问 commitId 作为 params.commitId

如果您在调用 wrapperPipeline() 时在闭包中有某些内容,那么这些内容将在 config 对象中。例如

wrapperPipeline({
    param="value"
})
Run Code Online (Sandbox Code Playgroud)

那么config.param会导致"value"

但是,作为建议,我建议在调用共享库中 vars/ 下存储的库时避免使用闭包。请参阅http://groovy-lang.org/closures.html了解什么是闭包。问题的关键是,它们相当复杂,如果由于闭包实例化而最终尝试传入动态变量,则可能会引入一些问题。(他们有自己的位置,但对于简单的事情,我认为避免更好)

我建议改为实现一个辅助函数,该函数将允许您使用映射或闭包来调用共享库。

在 src 路径下添加一个名为 buildConfig 的共享库:

package net.my.jenkins.workflow
import com.cloudbees.groovy.cps.NonCPS

class BuildConfig implements Serializable {
    static Map resolve(def body = [:]) {

        Map config = [:]
        config = body
        if (body in Map) {
            config = body
        } else if (body in Closure) {
            body.resolveStrategy = Closure.DELEGATE_FIRST
            body.delegate = config
            body()
        } else {
            throw  new Exception(sprintf("Unsupported build config type:%s", [config.getClass()]))
        }
        return config
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在 vars/ 下的共享库中以

import net.my.jenkins.workflow.BuildConfig

def call(def body = [:]) {
    // evaluate the body block, and collect configuration into the object
    config = BuildConfig.resolve(body)
Run Code Online (Sandbox Code Playgroud)

然后,这允许您使用消除复杂性的 Maps,因此您可以例如(不是那样,因为您只使用 params.commitId)重新分配它。

wrapperPipeline ([
    "commitId": params.commitId,
])
Run Code Online (Sandbox Code Playgroud)

这意味着config.commitId现在再次具有价值params.commitId

如果您需要更多详细信息,请告诉我。

TL;DR - 你应该使用 params 对象,因为你已经定义了参数。如果您确实开始通过共享 lib 调用传递参数,我将在闭包上使用映射。(需要一些最小的实现)