在 workflow-cps groovy 代码中获取 CpsScript 实例?

Obtain CpsScript instance in workflow-cps groovy code?

目前正在为非常具体的 jenkins 场景编码很多 groovy。

问题是我必须跟踪上下文的当前 CpsScript-实例(获取属性、环境等)及其 invokeMethod(工作流程步骤等).

目前这意味着我将管道 groovy 脚本中的 this 传递到我的条目 class 并从那里分别传递到每个 class,这是 很烦人

脚本实例由 CpsFlowExecution 创建并存储在 Continuable 实例和 CpsThreadGroup 中,两者都不允许您检索它。

似乎 GlobalVariable 派生扩展收到它,因此它们有一个上下文,但我目前的知识不足以编写我自己的扩展来利用它。

所以问题是:

有谁知道跟踪 CpsScript 实例的方法,不需要我将它传递给我创建的每个新 class 实例? (或者:从任何地方获取 - 这真的需要这么难吗?)

继续研究实现此目标的方法。甚至写了一个提供 cpsScript 全局变量的 jenkins 插件。不幸的是,您需要该实例为该调用提供上下文,因此它没有用。

因此,作为 "least bad solution"(tm),我创建了一个 class,我称之为 ScriptContext,我可以将其用作我的管道 class 的基础 classes(它实现了 Serializable)。

当你编写你的管道脚本时,你要么将它CpsScript静态传递一次:

ScriptContext.script = this

或者,如果您从它派生(确保调用 super()):

new MyPipeline(this) 

如果您的 class 来自 ScriptContext,您的工作就完成了。一切都会像您没有创建 class 而只是使用自动转换一样工作。如果您使用除 println 之外的任何 CpsScript 级函数,您可能也想在此处添加这些函数。

您可以在其他任何地方调用 ScriptContext.script 来获取脚本实例。

class 代码(删除了大部分注释以使其尽可能短):

package ...

import org.jenkinsci.plugins.workflow.cps.*

class ScriptContext implements Serializable {
    protected static CpsScript _script = null

    ScriptContext(CpsScript script = null) {
        if (!_script && script) {
            _script = script
        }
    }

    ScriptContext withScript(CpsScript script) {
        setScript(script)
        this
    }

    static void setScript(CpsScript script) {
        if (!_script && script) {
            _script = script
        }
    }

    static CpsScript getScript()
    {
        _script
    }

    // functions defined in CpsScript itself are not automatically found
    void println(what) {
        _script.println(what)
    }

    /**
     * For derived classes we provide missing method functionality by trying to 
     * invoke the method in script context
     */
    def methodMissing(String name, args) {
        if (!_script) {
            throw new GroovyRuntimeException('ScriptContext: No script instance available.')
        }
        return _script.invokeMethod(name, args)
    }

    /**
     * For derived classes we provide missing property functionality.
     * Note: Since it's sometimes unclear whether a property is an actual property or
     * just a function name without brackets, use evaluate for this instead of getProperty.
     * @param name
     * @param args
     * @return
     */
    def propertyMissing(String name) {
        if (!_script) {
            throw new GroovyRuntimeException('ScriptContext: No script instance available.')
        }
        _script.evaluate(name)
    }

    /**
     * Wrap in node if needed
     * @param body
     * @return
     */
    protected <V> V node(Closure<V> body) {
        if (_script.env.NODE_NAME != null) {
            // Already inside a node block.
            body()
        } else {
            _script.node {
                body()
            }
        }
    }
}