"Each" Jenkins 并行脚本问题中的闭包和 For 循环
"Each" Closure and For Loop in Jenkins Parallel script issue
我有一个基本上读取 JSON 文件并从中打印出一些值的 Jenkins 管道。
import groovy.json.JsonSlurper
import groovy.json.JsonSlurperClassic
pipeline {
agent any
stages {
stage('test') {
steps {
script {
def environment = new JsonSlurperClassic().parseText('''
{
"list": [
{
"name": "service1"
},
{
"name": "service2"
},
{
"name": "NotAService"
},
{
"name": "AnotherDummyService-mock",
}
]
}
'''
)
def forLoopBuilders = [:]
for (artifact in environment.list) {
if (!artifact.name.contains("-mock")) {
println("Before parallel, the value is ${artifact.name}")
forLoopBuilders[artifact.name] = { println(artifact.name) }
}
}
parallel forLoopBuilders
def closureBuilders = [:]
environment.list.each { artifact ->
if (!artifact.name.contains("-mock")) {
println("Before parallel, the value is ${artifact.name}")
closureBuilders[artifact.name] = { println(artifact.name) }
}
}
parallel closureBuilders
}
}
}
}
}
@NonCPS
def jsonParse(def json) {
new groovy.json.JsonSlurperClassic().parseText(json)
}
builders 变量是我存储并行 运行 阶段的方式。基本上是
[stageA: What to do in this stageA, anotherStage: What to do in this anotherStage]
输出结果如下
10:20:53 Before parallel, the value is service1
10:20:53 [Pipeline] echo
10:20:53 Before parallel, the value is service2
10:20:53 [Pipeline] echo
10:20:53 Before parallel, the value is NotAService
10:20:53 [Pipeline] parallel
10:20:53 [Pipeline] { (Branch: service1)
10:20:53 [Pipeline] { (Branch: service2)
10:20:53 [Pipeline] { (Branch: NotAService)
10:20:53 [Pipeline] echo
10:20:53 AnotherDummyService-mock
10:20:53 [Pipeline] }
10:20:53 [Pipeline] echo
10:20:53 AnotherDummyService-mock
10:20:53 [Pipeline] }
10:20:53 [Pipeline] echo
10:20:53 AnotherDummyService-mock
10:20:53 [Pipeline] // parallel
10:20:53 [Pipeline] echo
10:20:53 Before parallel, the value is service1
10:20:53 [Pipeline] echo
10:20:54 Before parallel, the value is service2
10:20:54 [Pipeline] echo
10:20:54 Before parallel, the value is NotAService
10:20:55 [Pipeline] parallel
10:20:55 [Pipeline] { (Branch: service1)
10:20:55 [Pipeline] { (Branch: service2)
10:20:55 [Pipeline] { (Branch: NotAService)
10:20:55 [Pipeline] echo
10:20:55 service1
10:20:55 [Pipeline] }
10:20:55 [Pipeline] echo
10:20:55 service2
10:20:55 [Pipeline] }
10:20:55 [Pipeline] echo
10:20:55 NotAService
如您所见,运行 并行阶段的输出是不同的。为什么会这样?
我想要的是并行 forLoopBuilders
的输出应该与 closureBuilders
.
的输出相同
这似乎是 for 循环中的闭包如何捕获循环变量的结果,在您的例子中 artifact
。
只有一个循环变量不断被重新赋值...所以闭包捕获那个单个变量,但是一旦循环结束,该变量将被分配最后一个值,因此所有闭包将只稍后查看该值。
您可以通过 运行 纯 Groovy:
中的这个简单示例来了解它的行为方式
def map = [
'Service1': 's1',
'Service2': 's2'
]
def closures = []
for (entry in map.entrySet()) {
closures << { println "ENTRY: $entry" }
}
closures*.call()
这将打印:
ENTRY: Service2=s2
ENTRY: Service2=s2
即闭包捕获 entry
变量的最后一个值。
Groovy 闭包在捕获值方面更智能,它们实际上在每个 运行 上创建新变量,因此如果您将 for 循环替换为 each { ... }
构造,它会起作用:
def map = [
'Service1': 's1',
'Service2': 's2'
]
def closures = []
map.entrySet().each { entry ->
closures << { println "ENTRY: $entry" }
}
closures*.call()
打印:
ENTRY: Service1=s1
ENTRY: Service2=s2
在您的情况下,只需使用 each { }
,它就可以满足您的需求。
编辑
如果你坚持使用for循环,你应该像在Java中那样做,并将当前值赋给一个新变量。
以下代码与使用 each
的结果相同:
def map = [
'Service1': 's1',
'Service2': 's2'
]
def closures = []
for (entry in map.entrySet()) {
def value = entry
closures << { println "ENTRY: $value" }
}
closures*.call()
我有一个基本上读取 JSON 文件并从中打印出一些值的 Jenkins 管道。
import groovy.json.JsonSlurper
import groovy.json.JsonSlurperClassic
pipeline {
agent any
stages {
stage('test') {
steps {
script {
def environment = new JsonSlurperClassic().parseText('''
{
"list": [
{
"name": "service1"
},
{
"name": "service2"
},
{
"name": "NotAService"
},
{
"name": "AnotherDummyService-mock",
}
]
}
'''
)
def forLoopBuilders = [:]
for (artifact in environment.list) {
if (!artifact.name.contains("-mock")) {
println("Before parallel, the value is ${artifact.name}")
forLoopBuilders[artifact.name] = { println(artifact.name) }
}
}
parallel forLoopBuilders
def closureBuilders = [:]
environment.list.each { artifact ->
if (!artifact.name.contains("-mock")) {
println("Before parallel, the value is ${artifact.name}")
closureBuilders[artifact.name] = { println(artifact.name) }
}
}
parallel closureBuilders
}
}
}
}
}
@NonCPS
def jsonParse(def json) {
new groovy.json.JsonSlurperClassic().parseText(json)
}
builders 变量是我存储并行 运行 阶段的方式。基本上是
[stageA: What to do in this stageA, anotherStage: What to do in this anotherStage]
输出结果如下
10:20:53 Before parallel, the value is service1
10:20:53 [Pipeline] echo
10:20:53 Before parallel, the value is service2
10:20:53 [Pipeline] echo
10:20:53 Before parallel, the value is NotAService
10:20:53 [Pipeline] parallel
10:20:53 [Pipeline] { (Branch: service1)
10:20:53 [Pipeline] { (Branch: service2)
10:20:53 [Pipeline] { (Branch: NotAService)
10:20:53 [Pipeline] echo
10:20:53 AnotherDummyService-mock
10:20:53 [Pipeline] }
10:20:53 [Pipeline] echo
10:20:53 AnotherDummyService-mock
10:20:53 [Pipeline] }
10:20:53 [Pipeline] echo
10:20:53 AnotherDummyService-mock
10:20:53 [Pipeline] // parallel
10:20:53 [Pipeline] echo
10:20:53 Before parallel, the value is service1
10:20:53 [Pipeline] echo
10:20:54 Before parallel, the value is service2
10:20:54 [Pipeline] echo
10:20:54 Before parallel, the value is NotAService
10:20:55 [Pipeline] parallel
10:20:55 [Pipeline] { (Branch: service1)
10:20:55 [Pipeline] { (Branch: service2)
10:20:55 [Pipeline] { (Branch: NotAService)
10:20:55 [Pipeline] echo
10:20:55 service1
10:20:55 [Pipeline] }
10:20:55 [Pipeline] echo
10:20:55 service2
10:20:55 [Pipeline] }
10:20:55 [Pipeline] echo
10:20:55 NotAService
如您所见,运行 并行阶段的输出是不同的。为什么会这样?
我想要的是并行 forLoopBuilders
的输出应该与 closureBuilders
.
这似乎是 for 循环中的闭包如何捕获循环变量的结果,在您的例子中 artifact
。
只有一个循环变量不断被重新赋值...所以闭包捕获那个单个变量,但是一旦循环结束,该变量将被分配最后一个值,因此所有闭包将只稍后查看该值。
您可以通过 运行 纯 Groovy:
中的这个简单示例来了解它的行为方式 def map = [
'Service1': 's1',
'Service2': 's2'
]
def closures = []
for (entry in map.entrySet()) {
closures << { println "ENTRY: $entry" }
}
closures*.call()
这将打印:
ENTRY: Service2=s2
ENTRY: Service2=s2
即闭包捕获 entry
变量的最后一个值。
Groovy 闭包在捕获值方面更智能,它们实际上在每个 运行 上创建新变量,因此如果您将 for 循环替换为 each { ... }
构造,它会起作用:
def map = [
'Service1': 's1',
'Service2': 's2'
]
def closures = []
map.entrySet().each { entry ->
closures << { println "ENTRY: $entry" }
}
closures*.call()
打印:
ENTRY: Service1=s1
ENTRY: Service2=s2
在您的情况下,只需使用 each { }
,它就可以满足您的需求。
编辑
如果你坚持使用for循环,你应该像在Java中那样做,并将当前值赋给一个新变量。
以下代码与使用 each
的结果相同:
def map = [
'Service1': 's1',
'Service2': 's2'
]
def closures = []
for (entry in map.entrySet()) {
def value = entry
closures << { println "ENTRY: $value" }
}
closures*.call()