Получить экземпляр CpsScript в коде workflow-cps groovy?

В настоящее время кодирую много groovy для очень специфических сценариев jenkins.

Проблема в том, что я должен отслеживать текущий CpsScript — экземпляр для контекста (получение свойств, среды и т. д.) и его invokeMethod (этапы рабочего процесса и т. п.).

В настоящее время это означает, что я передаю this в groovy-скрипт конвейера в свой начальный класс, а оттуда он передается каждому классу отдельно, что очень раздражает.

Экземпляр скрипта создается CpsFlowExecution и сохраняется в экземплярах Continuable и CpsThreadGroup, ни один из которых не позволяет вам его получить.

Кажется, что производные расширения GlobalVariable получают его, чтобы у них был контекст, но в настоящее время я недостаточно осведомлен, чтобы написать собственное расширение, чтобы использовать это.

Итак, вопрос:

Кто-нибудь знает способ отслеживать экземпляр CpsScript, который не требует от меня передачи его каждому новому классу, который я создаю? (Или в качестве альтернативы: получить его откуда угодно — неужели это действительно должно быть так сложно?)


person Blizz    schedule 13.09.2018    source источник


Ответы (1)


Продолжал искать способы добиться этого. Даже написал плагин jenkins, который предоставляет глобальную переменную cpsScript. К сожалению, вам нужен экземпляр, чтобы предоставить контекст для этого вызова, поэтому он бесполезен.

Итак, в качестве «наименее плохого решения» (tm) я создал класс, который я назвал ScriptContext, который я могу использовать в качестве базового класса для своих классов конвейера (он реализует Serializable).

Когда вы пишете свой сценарий конвейера, вы либо передаете ему CpsScript статически один раз:

ScriptContext.script = this

Или, если вы производные от него (обязательно вызовите super()):

new MyPipeline(this) 

Если ваш класс является производным от ScriptContext, ваша работа выполнена. Все будет работать так, как будто вы не создавали класс, а просто использовали автомагическое преобразование. Если вы используете какие-либо функции уровня CpsScript, кроме println, вы также можете добавить их сюда.

В любом другом месте вы можете просто вызвать ScriptContext.script, чтобы получить экземпляр скрипта.

Код класса (удалено большинство комментариев, чтобы сделать его как можно короче):

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()
            }
        }
    }
}
person Blizz    schedule 18.09.2018
comment
Удалось ли вам обойти тот факт, что подключаемый модуль безопасности сценария не позволяет вам вызывать invokeMethod() или propertyMissing(), или вы не используете класс ScriptContext в качестве базового класса для всего, что определено в Jenkinsfile или загружено с помощью шага загрузки? - person Joerg S; 25.02.2019
comment
Я решил работать с глобальной библиотекой вместо библиотеки папок. Базовый код содержит класс на основе ScriptContext для каждого конвейера. Поскольку глобальная библиотека является доверенной, песочница не применяется. Именно поэтому я так поступил. - person Blizz; 26.02.2019