У меня есть следующий код Jenkinsfile
:
#!groovydef projectPath = "${projectPath}"
def specPath = "${specPath}"
int numberOfRetries = "${NUM_OF_RETRIES}".toInteger()def failure = true
def retryAmount = 0
def start = System.currentTimeMillis()def getSpecName() {
specPath.split("/")[-1].split(".")[0]
}def getProjectPath() {
projectPath.split("/")[-1]
}def rmDocker() {
def remove = sh script: "docker rm -f cypress_${getSpecName()}", returnStatus: true
}stage("Cypress Setup") {
node("Cypress") {
rmDocker()
}
}stage("Cypress Run") {
node("Cypress") {
currentBuild.setDisplayName("${projectPath} - ${getSpecName()}")
while (failure && retryAmount < numberOfRetries) {
sh "docker pull dockreg.bluestembrands.com/cypresswithtests:latest"
if (getSpecName().toLowerCase().contains("auth")) {
exit_code = sh script:"docker run --name cypress_${getSpecName()} dockreg.bluestembrands.com/cypresswithtests:latest sh -c \"node SQLSite/request.js & cypress run -P ${projectPath} --spec ${specPath} --env RUN=${retryAmount} --config videoCompression=${videoCompression} --reporter /usr/local/lib/node_modules/mochawesome-cypress-bsb --reporter-options \"reportDir=mochawesome-reports/run${retryAmount}/, reportName=mochawesome\"\"", returnStatus: true
} else {
exit_code = sh script:"docker run --name cypress_${getSpecName()} dockreg.bluestembrands.com/cypresswithtests:latest sh -c \"cypress run -P ${projectPath} --spec ${specPath} --env RUN=${retryAmount} --config videoCompression=${videoCompression} --reporter /usr/local/lib/node_modules/mochawesome-cypress-bsb --reporter-options \"reportDir=mochawesome-reports/run${retryAmount}/, reportName=mochawesome\"\"", returnStatus: true
}
failure = exit_code != 0
try {
println "/var/docker-mounts/nfs/qa/test-results/${getProjectPath()}-${getSpecName()}/"
dir("/var/docker-mounts/nfs/qa/test-results/${getProjectPath()}-${getSpecName()}/") {
sh "docker cp cypress_${getSpecName()}:/cypress/${projectPath}/mochawesome-reports /var/docker-mounts/nfs/qa/test-results/${getProjectPath()}-${getSpecName()}/${BUILD_ID}"
}
} catch (Exception e) {
println e
echo "Failed to copy Mochawesome tests"
}
rmDocker()
retryAmount++
}
} if (failure) {
currentBuild.result = "FAILURE"
}
}
Он выдает следующее исключение, когда я пытаюсь его запустить:
java.lang.StackOverflowError: Excessively nested closures/functions at WorkflowScript.getProjectPath(WorkflowScript:16) - look for unbounded recursion - call depth: 1025
at com.cloudbees.groovy.cps.impl.CpsFunction.invoke(CpsFunction.java:28)
at com.cloudbees.groovy.cps.impl.CpsCallableInvocation.invoke(CpsCallableInvocation.java:40)
at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:62)
at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:109)
at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixName(FunctionCallBlock.java:77)
at sun.reflect.GeneratedMethodAccessor345.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
at com.cloudbees.groovy.cps.impl.ConstantBlock.eval(ConstantBlock.java:21)
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:174)
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:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Переменные:
NUM_OF_RETRIES: 3
specPath: bsb-haband-web / hab-shop / cypress / integration / Поиск / SearchNoResultsSpec.js
VideoCompression: false
projectPath: bsb-haband-web / hab-shop
BRANCH_NAME: master
Я не понимаю, где вызов рекурсии происходит как getProjectPath
, просто выполняет стандартный раздельный вызов.
Я попытался изменить его на .tokenize()
, но он все равно не работает.
может быть отмечено, что более одного из них могут запускаться в одно и то же время, однако ошибка возникает, даже если выполняется изолированно.
Не могли бы вы помочь мне понять, почему этот StackOverflowError
Вызов метода
getProjectPath()
вызывает это исключение. Это происходит, потому что если Groovy находит метод getter для поляfoo
, напримерgetFoo()
, он отказывается выполнять этот метод в любое время, когда видит доступ к значениюfoo
.Что это значит в вашем случае? Когда вы вызываете метод
, он запускается в бесконечную рекурсию, потому что этот метод рассматривается как:
, чтобы он никогда не достигал - поэтому замена его с помощью метода
.split("/")[-1]
не изменила ничего.Решение: переименовать
tokenize()
илиgetProjectPath()
.Свойства класса Groovy
Эта часть документации Groovy объясняет это поведение. Это может быть упрощено для примера - класс вроде:
скомпилирован примерно так:
Общее правило большого пальца при работе с Groovy заключается в том, что когда вы указываете поле
projectPath
, вы тщательно реализуетеfoo
(если вам действительно нужно это делать). Особенно вы избегаете доступа к полюgetFoo()
внутри этого метода, потому что он сталкивается с этой проблемой с бесконечным рекурсивным вызовом.