Jenkinsfile Pipeline DSL: как отображать несколько столбцов в графическом интерфейсе панели задач - для всех динамически создаваемых этапов - когда в разделе ТРУБОПРОВОД

Дженкинс 2.89.4 прокатка

Я видел почти все сообщения stackru, которые показывают, как мы можем успешно выполнять параллельные шаги / этапы (с использованием списка / карт и т.д.) - ИЛИ жестко их кодировать напрямую - ИЛИ даже создавать динамические этапы для Jenkinsfile (как показано в этом сообщении: Скриптовый параллельный этап jenkinsfile)

Мои требования:

  1. Конвейер, который строит N. no из проектов на этапах "BUILD", т.е. параллельно строит каждый из этих проектов. т.е. он запускает Gradle на всех N проектах. Здесь у меня есть файл Jenkins, созданный декларативным JOB DSL Groovy. Здесь мои проекты Gradle не настроены как многопроекты, поэтому я не могу вызвать gradle верхнего уровня и сказать: Gradle, пожалуйста, сделайте свою параллельную магию (в Gradle).

  2. Я хочу запустить сборку этих N проектов на отдельных отдельных параллельных динамически создаваемых этапах (столбцы графического интерфейса пользователя), как показано на панели управления заданием jenkins.

  3. Я хочу видеть вывод (сборка / консоль Gradle) каждой сборки проекта отдельно, т.е. я не хочу смешивать вывод консоли каждой сборки проектов, которые выполняются параллельно, только в ОДНОЙ КОЛОНКЕ (т.е. столбец с именемBUILD).

  4. В этом URL-адресе https://jenkins.io/blog/2017/09/25/declarative-1/ я вижу, как вы можете запускать параллельные этапы / этапы, но при этом либо он смешивает вывод этих параллельных шагов только в одном столбце (Я имею в виду столбец BUILD) - ИЛИ, если вы хотите, чтобы это было на отдельных этапах / столбцах (например, в сообщении говорится, что тестировать в Linux или Windows отдельно, тогда вы все еще жестко кодируете все этапы / шаги в Jenkinsfile на ранней стадии (вместо того, чтобы просто список или хэш массива, который я бы предпочел обновить для добавления большего или меньшего количества этапов / параллельных шагов, как в моем случае, все они следуют одному стандарту). Я хочу просто обновить в одном месте Сколько шагов и что все этапы в одном месте (список / массив).

  5. Я пока не использую 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 нет. проектов.

  1. Он будет запускать все проекты P для данного N- го шага параллельно.
  2. Он будет ждать, м шага для всех проектов, чтобы завершить первые, а затем перейти к следующему шагу энного

    Это означает, что если Шаг № 1 проекта ALPHA завершен, он все равно будет ждать всех Шагов № 1 других двух проектов, а затем запускать Шаг № 2 всех проектов параллельно.

  3. Задача: как мы можем запустить Шаг № 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
                }
            }
        }
    }
}
Другие вопросы по тегам