Jenkinsfile Pipeline DSL: как отображать несколько столбцов в графическом интерфейсе панели задач - для всех динамически создаваемых этапов - когда в разделе ТРУБОПРОВОД
Дженкинс 2.89.4 прокатка
Я видел почти все сообщения stackru, которые показывают, как мы можем успешно выполнять параллельные шаги / этапы (с использованием списка / карт и т.д.) - ИЛИ жестко их кодировать напрямую - ИЛИ даже создавать динамические этапы для Jenkinsfile (как показано в этом сообщении: Скриптовый параллельный этап jenkinsfile)
Мои требования:
Конвейер, который строит N. no из проектов на этапах "BUILD", т.е. параллельно строит каждый из этих проектов. т.е. он запускает Gradle на всех N проектах. Здесь у меня есть файл Jenkins, созданный декларативным JOB DSL Groovy. Здесь мои проекты Gradle не настроены как многопроекты, поэтому я не могу вызвать gradle верхнего уровня и сказать: Gradle, пожалуйста, сделайте свою параллельную магию (в Gradle).
Я хочу запустить сборку этих N проектов на отдельных отдельных параллельных динамически создаваемых этапах (столбцы графического интерфейса пользователя), как показано на панели управления заданием jenkins.
Я хочу видеть вывод (сборка / консоль Gradle) каждой сборки проекта отдельно, т.е. я не хочу смешивать вывод консоли каждой сборки проектов, которые выполняются параллельно, только в ОДНОЙ КОЛОНКЕ (т.е. столбец с именем
BUILD
).В этом URL-адресе https://jenkins.io/blog/2017/09/25/declarative-1/ я вижу, как вы можете запускать параллельные этапы / этапы, но при этом либо он смешивает вывод этих параллельных шагов только в одном столбце (Я имею в виду столбец BUILD) - ИЛИ, если вы хотите, чтобы это было на отдельных этапах / столбцах (например, в сообщении говорится, что тестировать в Linux или Windows отдельно, тогда вы все еще жестко кодируете все этапы / шаги в Jenkinsfile на ранней стадии (вместо того, чтобы просто список или хэш массива, который я бы предпочел обновить для добавления большего или меньшего количества этапов / параллельных шагов, как в моем случае, все они следуют одному стандарту). Я хочу просто обновить в одном месте Сколько шагов и что все этапы в одном месте (список / массив).
- Я пока не использую Jenkins Blue Ocean.
Обычно, если у вас есть параллельные шаги внутри стадии, их стандартный вывод консоли для всех шагов смешивается в один вывод консоли / стадию / столбец, когда вы щелкаете, чтобы увидеть вывод консоли для данного параллельного шага / стадии; Когда вы наводите указатель мыши на столбец СТРОИТЬ (при условии, что на этапе СТРОИТЬ были параллельные шаги) на панели инструментов задания (стандартный вывод для всех этих этапов смешан, и очень трудно увидеть вывод консоли отдельного этапа проекта только для данного этапа / этапа).
Если мы хотим создать отдельные этапы (динамически), тогда Jenkins должен иметь возможность отображать вывод консоли данного этапа / динамического этапа в параллельном разделе (т.е. каждый столбец должен отображать вывод консоли сборки своего собственного проекта).
Используя указанный выше URL-адрес, я могу сделать следующее после попытки этого сценария:
// main script block
// could use eg. params.parallel build parameter to choose parallel/serial
def runParallel = true
def buildStages
node('master') {
stage('Initializing Parallel Dynamic Stages') {
// Set up List<Map<String,Closure>> describing the builds
buildStages = prepareBuildStages()
println("Initialised pipeline.")
}
for (builds in buildStages) {
if (runParallel) {
parallel(builds)
} else {
// run serially (nb. Map is unordered! )
for (build in builds.values()) {
build.call()
}
}
}
stage('Done') {
println('The whole SHENZI is complete.')
}
}
// Create List of build stages to suit
def prepareBuildStages() {
def buildList = []
for (i=1; i<4; i++) {
def buildStages = [:]
for (name in [ 'Alpha', 'Tango', 'Chyarli' ] ) {
def n = "${name} ${i}"
buildStages.put(n, prepareOneBuildStage(n))
}
buildList.add(buildStages)
}
return buildList
}
def prepareOneBuildStage(String name) {
def proj_name = name.split(' ')[0]
def proj_parallel_sub_step = name.split(' ')[1]
//Return the whole chunkoni
return {
stage("Build\nProject-${proj_name}\nStep ${proj_parallel_sub_step}") {
println("Building ${proj_name} - ${proj_parallel_sub_step}")
sh(script:'sleep 15', returnStatus:true)
}
}
}
Когда я помещаю указанный выше Groovy Script (который создает ДИНАМИЧЕСКИЕ этапы) внутри Pipeline Script
или Pipeline Script from SCM
(то есть тот же код, доступный в файле.groovy) - он работает успешно и создает динамические этапы на этапе BUILD для каждого из 3 проектов и выполняет 3 шага (N-й) для всех 3 проектов параллельно, а затем запускает следующий N-й шаг на все 3 проекта и так далее.
Если вы видите ниже, у нас также есть отдельные столбцы на панели задач Jenkins для них.
Теперь, когда я помещаю вышеуказанный скрипт в Jenkinsfile (Pipeline DSL), где у меня есть pipeline { .... }
раздел, он не работает и дает мне следующую ошибку.
Используя свой JOB DSL, я создал новое задание Jenkins Pipeline, в котором Pipeline Script from SCM
вызывает файл groovy (который теперь содержит):
//----------------------------------------------------
// Both - Parallel Run and GUI View in JF Jenkins job.
//----------------------------------------------------
def runParallel = true
def buildStages
def wkspace = /var/lib/jenkins/workspaces/ignore_this_variale_or_its_value_for_now
// Create List of build stages to suit
def prepareBuildStages() {
def buildList = []
for (i=1; i<3; i++) {
def buildStages = [:]
for (name in [ 'Alpha', 'Tango', 'Chyarli' ] ) {
def n = "${name} ${i}"
buildStages.put(n, prepareOneBuildStage(n))
}
buildList.add(buildStages)
}
return buildList
}
//---
def prepareOneBuildStage(String name) {
def proj_name = name.split(' ')[0]
def proj_parallel_sub_step = name.split(' ')[1]
// return the whole chunkoni (i.e. for a given stage) - will be named dynamically.
return {
stage("Build\nProject-${proj_name}\nStep ${proj_parallel_sub_step}") {
println("Building ${proj_name} - ${proj_parallel_sub_step}")
sh(script:'sleep 15', returnStatus:true)
}
}
}
// Set up List<Map<String,Closure>> describing the builds
buildStages = prepareBuildStages()
//---------------------
String jenkinsBaselines
// SEE NOW --- we have this section called 'pipeline'
pipeline {
agent {
node {
label 'rhat6'
customWorkspace wkspace
}
}
options {
ansiColor('xterm')
timeout(time: 8, unit: 'HOURS')
skipDefaultCheckout()
timestamps()
}
environment {
someEnvVar = 'aValue'
}
//------------- Stages
stages {
stage('Initializing Parallel Dynamic Stages') {
// Set up List<Map<String,Closure>> describing the builds
println("Initialised pipeline.")
}
for (builds in buildStages) {
if (runParallel) {
parallel(builds)
} else {
// run serially (nb. Map is unordered! )
for (build in builds.values()) {
build.call()
}
}
}
stage('Done') {
println('The whole SHENZI is complete.')
}
}
//---------------------
}
Выполнение задания Jenkinsfile Jenkins теперь дает мне эту ошибку:
[BFA] Scanning build for known causes...
[BFA] No failure causes found
[BFA] Done. 1s
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 69: Not a valid stage section definition: "buildStages = prepareBuildStages()". Some extra configuration is required. @ line 69, column 5.
stage('Initializing Parallel Dynamic Stages') {
^
WorkflowScript: 69: Unknown stage section "println". Starting with version 0.5, steps in a stage must be in a steps block. @ line 69, column 5.
stage('Initializing Parallel Dynamic Stages') {
^
WorkflowScript: 75: Expected a stage @ line 75, column 5.
for (builds in buildStages) {
^
WorkflowScript: 86: Unknown stage section "println". Starting with version 0.5, steps in a stage must be in a steps block. @ line 86, column 5.
stage('Done') {
^
WorkflowScript: 69: No "steps" or "parallel" to execute within stage "Initializing Parallel Dynamic Stages" @ line 69, column 5.
stage('Initializing Parallel Dynamic Stages') {
^
WorkflowScript: 86: No "steps" or "parallel" to execute within stage "Done" @ line 86, column 5.
stage('Done') {
^
6 errors
at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310)
at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1085)
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:603)
at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:581)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:558)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:298)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:268)
at groovy.lang.GroovyShell.parseClass(GroovyShell.java:688)
at groovy.lang.GroovyShell.parse(GroovyShell.java:700)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:133)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:127)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:557)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:518)
at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:290)
at hudson.model.ResourceController.execute(ResourceController.java:97)
at hudson.model.Executor.run(Executor.java:429)
Finished: FAILURE
Как я могу заставить это работать в Jenkinsfile pipeline
раздел и по-прежнему может получать отдельные столбцы для динамически создаваемой стадии для данного проекта N и шага M?
Пробовал следующий способ, по-прежнему ошибки говорят.
//------------- Stages
stages {
stage('Initializing Parallel Dynamic Stages') {
// Set up List<Map<String,Closure>> describing the builds
buildStages = prepareBuildStages()
println("Initialised pipeline.")
// tried this way too. within a stage
buildStages.each { bld -->
parallel(bld)
}
}
stage('Done') {
println('The whole SHENZI is complete.')
}
}
//---------------------
2 ответа
Итак, я немного потрудился и заставил это работать.
Новый код в Jenkinsfile (для STAGES) теперь:
//------------- Stages
stages {
stage('Start Pipeline') {
steps {
script {
sh "echo HELLO moto razr!"
}
}
}
stage('Initializing Parallel Dynamic Stages'){
steps {
script {
// Run all Nth step for all Projects in Parallel.
buildStages.each { bs -> parallel(bs) }
// OR uncomment the following code (if conditional on boolean variable).
/*
for (builds in buildStages) {
if (runParallel) {
parallel(builds)
} else {
// run serially (nb. Map is unordered! )
for (build in builds.values()) {
build.call()
}
}
}
*/
}
}
}
stage('Done') {
println('The whole SHENZI is complete.')
}
}
//---------------------
Это все, что нужно для работы.
Для четких сообщений / названий этапов я также настроил функцию, и мы не будем устанавливать эту переменную buildStages в pipeline { ... }
//---
def prepareOneBuildStage(String name) {
def proj_name = name.split(' ')[0]
def proj_parallel_sub_step = name.split(' ')[1]
// return the whole chunkoni (i.e. for a given stage) - will be named dynamically.
return {
stage("BUILD Project-${proj_name} Parallel_Step_${proj_parallel_sub_step}") {
println("Parallel_Step # ${proj_parallel_sub_step} of Project => ${proj_name}")
sh(script:"echo \"Parallel_Step # ${proj_parallel_sub_step} of Project => ${proj_name}\" && sleep 20", returnStatus:true)
// -- OR -- you can call Gradle task i.e. rpm / any other / any other command here.
}
}
}
// Set up List<Map<String,Closure>> describing the builds. section now.
buildStages = prepareBuildStages()
//---------------------
Эта реализация сейчас создает N нет. из параллельных стадий не т.е. отдельный столбец для каждого проекта для данного шага N - го (при взгляде на приборную панель Jenkinsfile Иовом) для P нет. проектов.
- Он будет запускать все проекты P для данного N- го шага параллельно.
Он будет ждать, м шага для всех проектов, чтобы завершить первые, а затем перейти к следующему шагу энного
Это означает, что если Шаг № 1 проекта ALPHA завершен, он все равно будет ждать всех Шагов № 1 других двух проектов, а затем запускать Шаг № 2 всех проектов параллельно.
Задача: как мы можем запустить Шаг № 2 проекта ALPHA, как только этап № 1 проекта ALPHA будет завершен, то есть он не будет ждать завершения Шага 1 из двух других проектов и, возможно, сможет запустить Шаг № 2 проекта ALPHA 1 в параллельно с Шагом N(=1) или N+1 других проектов.
Это предполагает, что все проекты независимы друг от друга, и проекты не разделяют содержимое, созданное данным проектом / их этапом / шагами в любом другом проекте / этапе / шаге.
В зависимости от ваших собственных требований вы можете подождать (т.е. не запускать Шаг 2 всех проектов до тех пор, пока Шаг 1 всех проектов не будет полностью завершен) - ИЛИ - вы можете захотеть запустить Шаг 2 проекта ALPHA, скажем, - Шаг 2 из TANGO проекта, а шаг 1 проект CHYARLI по - прежнему продолжается.
Поскольку основная цель этой публикации заключалась в получении отдельных динамически создаваемых столбцов / этапов для каждого проекта (выполняемых параллельно в рамках pipeline { ... }
раздел), думаю, я получил то, что искал.
ПРИМЕЧАНИЕ. Не упустите возможность использовать параллель, если вы хотите запускать параллельные сборки конвейера. Для получения дополнительной информации о проблемах, связанных с бегом parallel
одновременное создание действий, см. здесь: Jenkins - java.lang.IllegalArgumentException: последний блок не имеет достаточно действительных битов и ошибка Gradle: задача 'null' не найдена в корневом проекте
Вы также можете запускать этапы параллельно, как показано ниже.
pipeline {
agent { node { label 'master' } }
stages {
stage('Add regression tests') {
steps {
script {
def testList = ["a", "b", "c", "d"]
def branches = [:]
for (int i = 0; i < 4 ; i++) {
int index=i, branch = i+1
stage ("branch_${branch}"){
branches["branch_${branch}"] = {
node ('master'){
sh "echo 'node: ${NODE_NAME}, index: ${index}, i: ${i}, testListVal: " + testList[index] + "'"
}
}
}
}
parallel branches
}
}
}
}
}