Jenkins管道外部的动态并联级';脚本';块

Jenkins管道外部的动态并联级';脚本';块,jenkins,groovy,jenkins-pipeline,Jenkins,Groovy,Jenkins Pipeline,我正在尝试动态地构建并行阶段,正如演示和演示的那样。具体而言,我尝试在管道范围外定义的函数中执行此操作,例如: pipeline{ stages{ stage('CI'){ steps{ doDynamicParallelSteps() } } } } def doDynamicParallelSteps(){ tests = [:] for (f in findFiles(glob: '**/html/*.html'))

我正在尝试动态地构建并行阶段,正如演示和演示的那样。具体而言,我尝试在管道范围外定义的函数中执行此操作,例如:

pipeline{
  stages{
    stage('CI'){
      steps{
        doDynamicParallelSteps()
      }
    }
  }
}

def doDynamicParallelSteps(){
  tests = [:]
  for (f in findFiles(glob: '**/html/*.html')) {
    tests["${f}"] = {
      node {
        stage("${f}") {
          echo '${f}'
        }
      }
    }
  }
  parallel tests
}
问题是,似乎只有当动态并行生成代码位于管道的steps{}块内的脚本{}块内时(如第一个源代码所示),这种方法才有效

运行类似于上述代码段的代码时,我在jenkins中看到了以下错误:

hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: java.lang.String.call() is applicable for argument types: (java.lang.String, org.jenkinsci.plugins.workflow.cps.CpsClosure2) values: [teststage, org.jenkinsci.plugins.workflow.cps.CpsClosure2@2e1b48b4]
Possible solutions: wait(), any(), trim(), size(), next(), size()
    at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:153)
    at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:155)
    at org.kohsuke.groovy.sandbox.impl.Checker.checkedCall(Checker.java:159)
    at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.methodCall(SandboxInvoker.java:17)
    at WorkflowScript.parallelHandler(WorkflowScript:1383)
    at ___cps.transform___(Native Method)
    at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:57)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:109)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:82)
    at sun.reflect.GeneratedMethodAccessor110.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
    at com.cloudbees.groovy.cps.impl.ClosureBlock.eval(ClosureBlock.java:46)
    at com.cloudbees.groovy.cps.Next.step(Next.java:83)
    at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:174)
    at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:163)
    at org.codehaus.groovy.runtime.GroovyCategorySupport$ThreadCategoryInfo.use(GroovyCategorySupport.java:122)
    at org.codehaus.groovy.runtime.GroovyCategorySupport.use(GroovyCategorySupport.java:261)
    at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:163)
    at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$101(SandboxContinuable.java:34)
    at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.lambda$run0$0(SandboxContinuable.java:59)
    at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox.runInSandbox(GroovySandbox.java:108)
    at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:58)
    at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:182)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:332)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$200(CpsThreadGroup.java:83)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:244)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:232)
    at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:64)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:131)
    at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
    at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:59)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Finished: FAILURE

有没有办法像我在初始代码片段中所展示的那样将其定义为函数,或者在管道定义中有大量的脚本{}块?

声明性管道不允许您将Groovy代码放入
步骤{}
block-此处需要有效的Jenkins管道步骤。这就是为什么引入了
script{}
块,它可以放在
steps{}
块中执行一些Groovy代码

如果您需要灵活性和非固执己见的语法,那么您可以使用脚本化管道。在这里,您可以将Groovy代码与现有的管道步骤混合使用,几乎没有任何限制

简要说明两种方法之间的差异以及它们存在的原因:

Jenkins Pipeline首次创建时,选择Groovy作为基础。Jenkins长期以来一直提供嵌入式Groovy引擎,为管理员和用户提供高级脚本功能。此外,詹金斯管道的实现者发现Groovy是建立现在称为“脚本流水线”DSL的坚实基础。[2]

由于它是一个功能齐全的编程环境,脚本化管道为Jenkins用户提供了极大的灵活性和可扩展性。Groovy学习曲线通常不适合给定团队的所有成员,因此创建声明性管道是为了为编写Jenkins管道提供一种更简单、更固执己见的语法

两者基本上都是相同的管道子系统。它们都是“作为代码的管道”的持久实现。它们都能够使用管道中内置的步骤或插件提供的步骤。两者都能够利用共享库

脚本化管道中的示例可能如下所示:

node {
  stage('CI') {
    doDynamicParallelSteps()
  }
}

def doDynamicParallelSteps(){
  tests = [:]
  for (f in findFiles(glob: '**/html/*.html')) {
    tests["${f}"] = {
      node {
        stage("${f}") {
          echo '${f}'
        }
      }
    }
  }
  parallel tests
}
pipeline{
  agent any
  stages{
    stage('CI'){
      steps{
        script {
            doDynamicParallelSteps()
        }
      }
    }
  }
}

def doDynamicParallelSteps(){
  tests = [:]
  for (f in findFiles(glob: '**/html/*.html')) {
    tests["${f}"] = {
      node {
        stage("${f}") {
          echo '${f}'
        }
      }
    }
  }
  parallel tests
}
步骤中带有
script{}
块的声明性管道如下所示:

node {
  stage('CI') {
    doDynamicParallelSteps()
  }
}

def doDynamicParallelSteps(){
  tests = [:]
  for (f in findFiles(glob: '**/html/*.html')) {
    tests["${f}"] = {
      node {
        stage("${f}") {
          echo '${f}'
        }
      }
    }
  }
  parallel tests
}
pipeline{
  agent any
  stages{
    stage('CI'){
      steps{
        script {
            doDynamicParallelSteps()
        }
      }
    }
  }
}

def doDynamicParallelSteps(){
  tests = [:]
  for (f in findFiles(glob: '**/html/*.html')) {
    tests["${f}"] = {
      node {
        stage("${f}") {
          echo '${f}'
        }
      }
    }
  }
  parallel tests
}

脚本的第1383行是什么?根据错误消息,错误似乎是由那里引起的。出于某种原因,这对我不起作用。我已经将该块的一个更简单的版本复制到我的管道文件中,但它仍然无法工作,并且在执行
并行测试时抛出与上面相同的错误。出于某种原因,它在stacktrace中指向的行实际上是dynamic parallel steps函数中的阶段声明。最奇怪的是,我在同一个jenkins实例中创建了一个单独的示例管道,并且成功了。所以我猜这与我的特定管道文件有关,但我甚至不知道如何开始调试。所以,我的一位同事发现了错误,这真的很愚蠢。。。原始函数定义的一个参数(与代码段不同)称为“stage”,类型为String。这样我就可以在管道中调用函数的哪个阶段的上下文中传递。问题是,这个“stage”变量正在覆盖
节点{stage(“…”{…}}
块中的stage()声明。这也是我们在堆栈跟踪中看到错误的原因,jenkins抱怨String没有方法调用()。非常令人谦卑的体验。@szymon stepniak需要注意的是,动态步骤中的阶段不会在正确的git引用中运行(假设为git):声明性管道在正常步骤中处理引用更新,但在此动态阶段中不会做任何事。这不是一个明显的错误,因此可能值得一提:)在我的例子中,我还需要在
for
循环之间添加
for
tests
字典中,并在后台使用
var
,否则我总是得到
findFiles
中所有线程的最后一个元素(在我的例子中,我使用了数组列表)我间歇性地得到下面的异常****被标记为脱机:连接被中断:java.io.EOFException…..hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:36)在hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:63)导致:java.io.IOException:hudson.remoting.SynchronousCommandTransport$ReaderThread.run处的通道意外终止(SynchronousCommandTransport.java:77)