【问题标题】:Can I create dynamically stages in a Jenkins pipeline?我可以在 Jenkins 管道中动态创建阶段吗?
【发布时间】:2017-08-07 19:17:24
【问题描述】:

我需要在声明式管道中启动一组动态测试。 为了更好的可视化目的,我想为每个测试创建一个阶段。 有办法吗?

我所知道的创建舞台的唯一方法是:

stage('foo') {
   ...
}

我见过this example,但我没有使用声明式语法。

【问题讨论】:

    标签: jenkins jenkins-pipeline


    【解决方案1】:

    使用比声明式语法更灵活的脚本化语法,即使声明式更有文档记录和推荐。

    例如可以循环创建阶段:

    def tests = params.Tests.split(',')
    for (int i = 0; i < tests.length; i++) {
        stage("Test ${tests[i]}") {
            sh '....'
        }
    }
    

    【讨论】:

    • @haridsv 对于显示管道,它是否单独显示阶段?
    • @codeGeass 是的,而且它似乎也正确地计算了时间(即它没有将时间聚合到Initialize 阶段)。
    • 您可以使用 Blue Ocean 进行可视化吗?因为我刚刚得到 Initialize 阶段,其他阶段就像那个阶段的步骤。
    • 这似乎不起作用。你能提供一个完整的例子吗?据我了解,我们需要创建一个管道项目,然后 sn-p 不起作用......
    • 代码有错字(已修复)。这对我有用,它根据输入参数创建阶段。您需要一个名为“Tests”的输入参数,它是一个逗号 (',') 分隔的测试用例列表。概念问题:如果您更改输入,它会创建新阶段并且您会丢失历史记录。
    【解决方案2】:

    正如 JamesD 所建议的,您可以像这样动态创建阶段(但它们将是连续的):

    def list
    pipeline {
        agent none
        options {buildDiscarder(logRotator(daysToKeepStr: '7', numToKeepStr: '1'))}
        stages {
            stage('Create List') {
                agent {node 'nodename'}
                steps {
                    script {
                        // you may create your list here, lets say reading from a file after checkout
                        list = ["Test-1", "Test-2", "Test-3", "Test-4", "Test-5"]
                    }
                }
                post {
                    cleanup {
                        cleanWs()
                    }
                }
            }
            stage('Dynamic Stages') {
                agent {node 'nodename'}
                steps {
                    script {
                        for(int i=0; i < list.size(); i++) {
                            stage(list[i]){
                                echo "Element: $i"
                            }
                        }
                    }
                }
                post {
                    cleanup {
                        cleanWs()
                    }
                }
            }
        }
    }
    
    

    这将导致: dynamic-sequential-stages

    【讨论】:

    • 非常感谢,伙计!作为for (...) { 的替代品,我可以使用list.each { listItem -&gt;。我认为list.eachWithIndex { listItem, i -&gt; 也适用于碰巧需要索引的人。
    • 我们可以在 for 循环中使用多个阶段吗?
    【解决方案3】:

    @Jorge Machado:因为我无法发表评论,所以我不得不将其发布为答案。我最近解决了。希望对你有帮助。

    声明性管道:

    一个简单的静态示例:

    stage('Dynamic') {
            steps {
                script {
                    stage('NewOne') {
    
                            echo('new one echo')
    
                    }
                }
            }
        }
    

    动态真实示例:

        // in a declarative pipeline
            stage('Trigger Building') {
                  when {
                    environment(name: 'DO_BUILD_PACKAGES', value: 'true')
                  }
                  steps {
                    executeModuleScripts('build') // local method, see at the end of this script
                  }
                }
    
    
        // at the end of the file or in a shared library
            void executeModuleScripts(String operation) {
    
              def allModules = ['module1', 'module2', 'module3', 'module4', 'module11']
    
              allModules.each { module ->  
              String action = "${operation}:${module}"  
    
              echo("---- ${action.toUpperCase()} ----")        
              String command = "npm run ${action} -ddd"                   
    
                // here is the trick           
                script {
                  stage(module) {
                    bat(command)
                  }
                }
              }
    
    }
    

    【讨论】:

    • 感谢您发布此信息!我没有像我应该的那样关注您的回复,因为内部舞台定义中缺少steps 块非常重要。希望这可以帮助那些只是略读而不是真正阅读的人:)
    【解决方案4】:

    你可能想看看this example - 你可以让一个函数返回一个闭包,应该在其中有一个阶段。

    此代码显示了概念,但其中没有阶段。

    def transformDeployBuildStep(OS) {
        return {
            node ('master') { 
            wrap([$class: 'TimestamperBuildWrapper']) {
    ...
            } } // ts / node
        } // closure
    } // transformDeployBuildStep
    
    stage("Yum Deploy") {
      stepsForParallel = [:]
      for (int i = 0; i < TargetOSs.size(); i++) {
          def s = TargetOSs.get(i)
          def stepName = "CentOS ${s} Deployment"
          stepsForParallel[stepName] = transformDeployBuildStep(s)
      }
      stepsForParallel['failFast'] = false
      parallel stepsForParallel
    } // stage
    

    【讨论】:

    • @Aaron,在我创建数组之后,我怎么能不并行运行它?
    • 我不知道如何不并行执行。你为什么要?只需在需要时执行您需要的操作?
    • 有人在将近三年后才投票支持我并把我带回来 - 我上面的示例代码现在已经开源 - github.com/opencpi/opencpi/blob/develop/releng/jenkins/runtime/…
    • 您可以在返回的实例上使用 .call() 进行非并行构建。
    【解决方案5】:

    如果您不想使用for 循环,并且生成的管道要并行执行,那么这里有一个答案。

    def jobs = ["JobA", "JobB", "JobC"]
     
    def parallelStagesMap = jobs.collectEntries {
        ["${it}" : generateStage(it)]
    }
     
    def generateStage(job) {
        return {
            stage("stage: ${job}") {
                    echo "This is ${job}."
            }
        }
    }
     
    pipeline {
        agent none
     
        stages {
            stage('non-parallel stage') {
                steps {
                    echo 'This stage will be executed first.'
                }
            }
     
            stage('parallel stage') {
                steps {
                    script {
                        parallel parallelStagesMap
                    }
                }
            }
        }
    }
    

    Note 所有生成的阶段都将在 1 个节点中执行。 如果您愿意将生成的阶段执行到不同的节点中。

    def agents  = ['master', 'agent1', 'agent2']
    // enter valid agent name in array.
    
    def generateStage(nodeLabel) {
        return {
            stage("Runs on ${nodeLabel}") { 
                node(nodeLabel) {
                    echo "Running on ${nodeLabel}"
                }
            }
        }
    }
    def parallelStagesMap = agents.collectEntries {
        ["${it}" : generateStage(it)]
    }
    pipeline {
        agent none
        stages {
            stage('non-parallel stage') {
                steps {
                    echo 'This stage will be executed first.'
                }
            }
    
            stage('parallel stage') {
                steps {
                    script {
                        parallel parallelStagesMap
                    }
                }
            }        
        }
    }
    

    您当然可以添加多个参数,并且可以将collectEntries 用于两个参数。

    请记住函数generateStage中的return是必须的。

    【讨论】:

    • 简洁,适用于 Jenkins 2.263.4
    【解决方案6】:

    我使用它来生成包含 Jenkins 作业的阶段。 build_list 是我想从我的主要 Jenkins 作业触发的 Jenkins 作业列表,但每个触发作业都有一个阶段。

    build_list = ['job1', 'job2', 'job3']
            for(int i=0; i < build_list.size(); i++) {
              stage(build_list[i]){
                   build job: build_list[i], propagate: false
              }
            }
    

    【讨论】:

      【解决方案7】:

      只是对@np2807@Anton Yurchenko 已经介绍的内容的补充:您可以通过简单地延迟创建阶段列表(但保留其声明)来动态创建阶段并并行运行,例如像这样:

      def parallelStagesMap
      
      def generateStage(job) {
          return {
              stage("stage: ${job}") {
                      echo "This is ${job}."
              }
          }
      }
       
      pipeline {
          agent { label 'master' }
       
          stages {
              stage('Create List of Stages to run in Parallel') {
                  steps {
                      script {
                          def list = ["Test-1", "Test-2", "Test-3", "Test-4", "Test-5"]
                          // you may create your list here, lets say reading from a file after checkout
                          // personally, I like to use scriptler scripts and load the as simple as:
                          // list = load '/var/lib/jenkins/scriptler/scripts/load-list-script.groovy'
                          parallelStagesMap = list.collectEntries {
                              ["${it}" : generateStage(it)]
                          }
                      }
                  }
              }
       
              stage('Run Stages in Parallel') {
                  steps {
                      script {
                          parallel parallelStagesMap
                      }
                  }
              }
          }
      }
      

      这将导致动态并行阶段:

      【讨论】:

        【解决方案8】:

        如果您当时使用的是 Jenkinsfile,我通过动态创建阶段、并行运行它们以及让 Jenkinsfile UI 显示单独的列来实现它。这假设并行步骤彼此独立(否则不要使用并行),并且您可以将它们嵌套到任意深度(取决于您为创建阶段而嵌套的 for 循环数)。

        Jenkinsfile Pipeline DSL: How to Show Multi-Columns in Jobs dashboard GUI - For all Dynamically created stages - When within PIPELINE section查看更多信息。

        【讨论】:

          猜你喜欢
          • 2023-01-21
          • 2022-10-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-05
          相关资源
          最近更新 更多