| People who get stuck here, check for WorkflowScript in your stack traces. That's your Jenkinsfile. This has caused me a lot of difficulty as I learn to work with pipelines, though knowing the above makes life much easier. So a big +1 for improving it. *I worked around it in my pipelines; see below for code*. The simplest improvement would be to use [StackTraceUtils](http://docs.groovy-lang.org/latest/html/api/org/codehaus/groovy/runtime/StackTraceUtils.html) to sanitize the Groovy stuff. It could also use `StackTraceUtils.addClassTest(...)` to filter out some limited Jenkins and pipeline internals, though some care is needed not to remove important info "above" the application failure. Can't Jenkins potentially just walk the stack trace and transform `WorkflowScript` to the `${pipeline-script}` name, or annotate the name with the script-name? That alone would be a huge help, and pretty simple. Bonus points for abbreviating stack traces at `WorkflowScript.run`with a `...` - the rest of the stack isn't really that interesting if you know you got to the point of successful pipeline execution. I tried to work around it in my Groovy scripts with an outer try/catch block in which I ran:
try {
// pipeline here
} catch (e) {
currentBuild.result = 'FAILURE'
StackTraceUtils.sanitize(e)
throw e
}
... but it doesn't seem to remove anything much. Even when I add my own class filters (see below). Using `StackTraceUtils.printSanitizedStackTrace(e)` has the same effect. Note that the docs for StackTraceUtils.sanitize appear to be wrong. It doesn't return the exception - it returns null. Attempting to
// this is wrong, do not do this
throw StackTraceUtils.sanitize(e)
will yield the amazingly uninformative exception:
java.lang.NullPointerException
at com.cloudbees.groovy.cps.impl.ThrowBlock$1.receive(ThrowBlock.java:42)
....
when you try to throw null. I also wrote a filter to cut some of the crud out:
StackTraceUtils.addClassTest({
def result
switch (it) {
/* The Jenkinsfile itself */
case 'WorkflowScript':
result = true
break
/* Innards we don't care about */
case ~/^org.jenkinsci.plugins.workflow/:
result = false
break
case ~/^org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox/:
result = false
break
case ~/^hudson.remoting/:
result = false
break
case ~/^jenkins.util.ContextResettingExecutorService/:
result = false
break
case ~/^jenkins.security.ImpersonatingExecutorService/:
result = false
break
default:
result = null
break
}
return result
})
... but it's not doing much yet. Note that you'll have to approve the required static methods in Jenkins script approvals. (You'll be prompted in the console output on build failure). Or you can just add
staticMethod org.codehaus.groovy.runtime.StackTraceUtils addClassTest groovy.lang.Closure
staticMethod org.codehaus.groovy.runtime.StackTraceUtils sanitize java.lang.Throwable
in /jenkins/scriptApproval/ . So in short, why is a simple "undefined variable" quite this verbose and hard? |