Почему @NonCPS необходим при переборе по списку?

#jenkins #groovy #jenkins-pipeline

#дженкинс #groovy #дженкинс-конвейер

Вопрос:

У меня есть следующий заводной код:

 // @NonCPS
def printList(params) {
    def jobs = [:]
    println params
    params.split(",").each { param ->
        println "Param: ${param}"
    }
}
text = "Foo,Bar,Baz,Qux"
printList(text)
  

который работает должным образом при вызове из groovy команды:

 $ groovy test.groovy 
Foo,Bar,Baz,Qux
Param: Foo
Param: Bar
Param: Baz
Param: Qux
  

Хотя при тестировании в рамках задания без изолированной среды в Jenkins я получаю разные результаты:

 [Pipeline] echo
Foo,Bar,Baz,Qux
[Pipeline] echo
Param: Foo
[Pipeline] End of Pipeline
  

Проблема в том, что печатается только первый элемент, а не все.

Однако код работает так, как ожидалось в Jenkins, только при использовании @NonCPS вспомогательного метода.

Почему @NonCPS это необходимо для правильной итерации по списку элементов? И есть ли какой-либо обходной путь без использования @NonCPS (особенно в изолированной среде)?

Комментарии:

1. Я не буду пытаться объяснить вам прямо сейчас, что CPS работает в конвейере, его реализация все еще немного волшебна / непредсказуема для меня и не связана с этим вопросом. Тем не менее, я могу сказать, что конвейер без изолированной среды сильно отличается от изолированной версии того же скрипта. Вероятно, это проблемы с реализацией, как мне кажется. Конвейер Дженкинса все еще находится в стадии интенсивной разработки. Кстати, опция «отключить изолированную среду» недоступна для scipts с SCM-исходным кодом по очевидным причинам

2. Я предлагаю вам сосредоточиться на режиме «изолированный», устраняя проблемы и ограничения, которые у него есть, по мере их появления. Это основной режим для конвейера

Ответ №1:

Вы также можете использовать другие методы для получения того же эффекта. Например, следующее является сериализуемым и не требует, чтобы @NonCPS выполнял итерацию по списку с именем mylist:

 for(int i=0; i < mylist.size(); i  ) {
  println mylist[i]
}
  

Ответ №2:

Насколько я понимаю, как CPS работает и реализован в целом, и как он обрабатывается конвейером (пока не углублялся в его код), конвейер пытается проверить способность приостанавливать (резервное копирование / .. передача .. / восстановление) состояние выполнения (JVM) в большинстве инструкций кода через интерфейс сериализации. Учитывая, что это JVM < groovy < pipeline DSL .. разработчикам плагинов требуется много усилий для реализации.

Далее, не будучи сам гуру Groovy или Java, насколько я могу судить о его реализации, генераторы groovy (например, { f -> print f } ) очень похожи на генераторы python (yield) или C / C (объекты-функторы).

Пока они не могут быть сериализованы конвейером с легкостью, если итератор объекта итерации (например, индекс списка или состояние выражения выхода) является неявным. Таким образом, предпочтение циклов в стиле Java.

Альтернативные циклы в стиле Java (классический C) намного проще обрабатывать, если область выполнения явно содержит итератор цикла (индексную переменную ‘i’).

Пожалуйста, не считайте это авторитетным ответом, вышеупомянутое — это просто мое личное понимание CPS конвейера