Jenkins升级至2.319后Pipeline读取工作区文件触发空指针异常
问题背景
在Jenkins 2.187旧版本环境中,以下Pipeline脚本可以正常拉取Git仓库全量文件,核心逻辑如下:
node { stage('checkout spring-cloud-config') { dir('config') { checkout scm } } stage('csv -> yml') { def files = new File("${workspace}/config/security").listFiles() for(int i = 0; i < files.size(); i ++){ ... }
将Jenkins升级迁移到2.319版本后,同一段脚本运行失败,报错日志如下:
[Pipeline] } [Pipeline] // dir [Pipeline] } [Pipeline] // stage [Pipeline] stage [Pipeline] { (csv -> yml) [Pipeline] } [Pipeline] // stage [Pipeline] } [Pipeline] // node [Pipeline] End of Pipeline java.lang.NullPointerException: Cannot invoke method size() on null object at org.codehaus.groovy.runtime.NullObject.invokeMethod(NullObject.java:91) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:48) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48) at org.codehaus.groovy.runtime.callsite.NullCallSite.call(NullCallSite.java:35) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113) at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:163) at org.kohsuke.groovy.sandbox.impl.Checker.checkedCall(Checker.java:165) at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.methodCall(SandboxInvoker.java:17) at WorkflowScript.run(WorkflowScript:16) at ___cps.transform___(Native Method) at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:86) at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:113) at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixName(FunctionCallBlock.java:78) at jdk.internal.reflect.GeneratedMethodAccessor263.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) 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:129) at org.codehaus.groovy.runtime.GroovyCategorySupport.use(GroovyCategorySupport.java:268) at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:163) at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$001(SandboxContinuable.java:18) at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:51) at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:185) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:403) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$400(CpsThreadGroup.java:97) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:315) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:279) at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:67) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:139) at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28) at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:68) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at java.base/java.lang.Thread.run(Thread.java:829) Finished: FAILURE
需要确认该问题是Groovy版本更新导致,还是缺少相关Jenkins插件引发。
问题根因
这个报错既不是Groovy版本更新导致,也不是缺少核心运行插件,本质是高版本Jenkins收紧了Pipeline脚本安全沙箱规则、强化了分布式构建上下文隔离,原有脚本的不规范写法在新版本下不再兼容:
- Pipeline中直接使用Groovy原生
new File()操作文件属于错误用法:这类原生文件API直接读取Jenkins控制器节点的本地文件系统,不会自动识别当前构建任务所在的代理节点工作目录,也不在脚本安全沙箱的默认放行白名单里。高版本Jenkins默认开启严格沙箱模式,这类操作要么被权限拦截,要么读取到错误的路径,当listFiles()找不到对应路径时会直接返回null,后续调用size()就会触发空指针。 - 硬拼接
${workspace}/config/security路径不可靠:高版本Pipeline中dir('config')步骤切换的是Pipeline步骤级别的相对工作路径,不会直接在控制器文件系统生成对应层级的物理目录,拼接出来的路径本身不存在,自然无法获取文件列表。
修复方案
- 彻底弃用Pipeline中的原生
new File()文件操作,统一使用Jenkins官方提供的Pipeline内置步骤处理文件,这类步骤会自动适配当前构建节点的工作空间,默认兼容沙箱规则。 - 遍历文件优先使用
findFiles步骤(依赖Pipeline Utility Steps插件,属于Pipeline生态常用基础插件,高版本环境一般默认预装),调用返回结果前增加空值判断,避免空指针。 - 修正后的参考代码:
node { stage('checkout spring-cloud-config') { dir('config') { checkout scm } } stage('csv -> yml') { // 自动匹配当前工作空间下的对应路径,兼容分布式构建场景 def files = findFiles(glob: 'config/security/*') if (files) { for(int i = 0; i < files.size(); i ++){ // 后续文件读写、处理逻辑统一使用readFile、writeFile等内置步骤实现 // ... } } } }
内容的提问来源于stack exchange,提问作者Broshet
相关产品推荐
相关产品推荐

