将参数从 Jenkinsfile 传递到共享库 [英] Passing parameters from Jenkinsfile to a shared library

查看:16
本文介绍了将参数从 Jenkinsfile 传递到共享库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have several components(code projects with their own Bitbucket repositories) and each of them has a Jenkinsfile as follows:

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
    }
}

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

Need to use a 'wrapper' pipeline, say, 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
        }

    }
}

修改后的 Jenkinsfile:

The modified 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{}

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

Now, I suspect that the params object(the properties from the Jenkinsfile) is not populated correctly. For example

def buildInfoCommitId = config.commitId
.
.
.
println "buildInfoCommitId:  "+buildInfoCommitId

打印空值.

如何正确调用 wrapperPipeline?

How do I invoke the wrapperPipeline correctly?

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

Note: I am new to both Jenkins pipelines and Groovy :)

推荐答案

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

Because those are Jenkins Parameters, they are not in the config object.

您将以 params.commitId

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

If you had something within the closure when you call wrapperPipeline(), then those would be in the config object. e.g.

wrapperPipeline({
    param="value"
})

然后 config.param 将导致 "value"

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

However, as a word of advice, I recommend avoiding using a closure when calling libs stored under vars/ in the shared library. See http://groovy-lang.org/closures.html for what closures are. The crux of it is, they are fairly complicated and can introduce some issues if you end up trying to pass in dynamic variables due to when the closure is instantiated. (They have their place but for simple things, I think avoiding is better)

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

I'd recommend instead, implementing a helper function that will allow you use maps OR closures for calling shared libs.

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

add a shared library called buildConfig under your src path:

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
    }
}

然后在你的共享库下的 vars/开始

And then in your shared lib under vars/ start with

import net.my.jenkins.workflow.BuildConfig

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

然后,您可以使用 Maps 来消除复杂性,因此您可以重新分配它(不是这样,因为您只会使用 params.commitId).

This then allows you to use Maps which removes the complexity, so you could for instance (not that you would since you would just use params.commitId) re-assign it.

wrapperPipeline ([
    "commitId": params.commitId,
])

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

Which means again config.commitId now has the value of params.commitId

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

Let me know if you need more detail.

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

TL;DR - You should be using params object, because you have parameters defined. If you did start passing in arguments via the shared lib call, I would use a map over a closure. (requires some minimal implementation)

这篇关于将参数从 Jenkinsfile 传递到共享库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆