【问题标题】:File not found in Jenkins pipeline script在 Jenkins 管道脚本中找不到文件
【发布时间】:2018-10-10 09:19:15
【问题描述】:

我正在尝试让当前在我们的 Jenkins 主机上运行的管道脚本,在远程 Jenkins 节点上执行。但是我遇到了一个奇怪的FileNotFound 异常。

我能够重现该问题的管道的最基本版本是:

node("remoteNode") {
env.SERVICE_VERSIONS_FILE = pwd() + '/service_versions.csv'
stage('Read file') {
  git credentialsId: '***', url: '***'      
  sh "cat $env.SERVICE_VERSIONS_FILE"
  new File(env.SERVICE_VERSIONS_FILE).each { line ->
    echo "$line"
   }
  }
}

结果:

>java.io.FileNotFoundException: /home/***/workspace/DeploymentPipelines/test-deployer/service_versions.csv
> (No such file or directory)   at java.io.FileInputStream.open0(Native
> Method)   at java.io.FileInputStream.open(FileInputStream.java:195)   at
> java.io.FileInputStream.<init>(FileInputStream.java:138)  at
> groovy.util.CharsetToolkit.<init>(CharsetToolkit.java:71)     at
> org.codehaus.groovy.runtime.ResourceGroovyMethods.newReader(ResourceGroovyMethods.java:1572)
>   at
> org.codehaus.groovy.runtime.ResourceGroovyMethods.readLines(ResourceGroovyMethods.java:533)
>   at
> org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.asCollection(DefaultTypeTransformation.java:461)
>   at
> org.codehaus.groovy.runtime.DefaultGroovyMethods.iterator(DefaultGroovyMethods.java:15955)
>   at org.codehaus.groovy.runtime.dgm$367.doMethodInvoke(Unknown Source)
>   at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1213)
>   at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
>   at
> org.codehaus.groovy.runtime.InvokerHelper.invokePojoMethod(InvokerHelper.java:913)
>   at
> org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:904)
>   at
> org.codehaus.groovy.runtime.InvokerHelper.asIterator(InvokerHelper.java:573)
>   at org.codehaus.groovy.runtime.InvokerHelper$asIterator.call(Unknown
> Source)   at
> org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
>   at
> org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
>   at
> com.cloudbees.groovy.cps.sandbox.DefaultInvoker.methodCall(DefaultInvoker.java:20)
>   at
> com.cloudbees.groovy.cps.CpsDefaultGroovyMethods.each(CpsDefaultGroovyMethods:1890)
>   at WorkflowScript.run(WorkflowScript:8)     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.GeneratedMethodAccessor324.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.LocalVariableBlock$LocalVariable.get(LocalVariableBlock.java:39)
>   at
> com.cloudbees.groovy.cps.LValueBlock$GetAdapter.receive(LValueBlock.java:30)
>   at
> com.cloudbees.groovy.cps.impl.LocalVariableBlock.evalLValue(LocalVariableBlock.java:28)
>   at
> com.cloudbees.groovy.cps.LValueBlock$BlockImpl.eval(LValueBlock.java:55)
>   at com.cloudbees.groovy.cps.LValueBlock.eval(LValueBlock.java:16)   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.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

sh "cat $env.SERVICE_VERSIONS_FILE" 返回正确的结果(即 - 它打印文件的内容)。

在主服务器上执行时,管道工作正常。感觉我可能错过了一些极其简单的东西?还是bug?

【问题讨论】:

    标签: jenkins-pipeline jenkins-groovy


    【解决方案1】:

    我不太确定它的工作原理或原因,但我发现了这个 https://*.com/a/38679858/985291 并通过使用提到的 readFile 步骤 https://jenkins.io/doc/pipeline/steps/workflow-basic-steps/#readfile-read-file-from-workspace 设法解决了这个问题。

    所以基本上,改变

     new File(env.SERVICE_VERSIONS_FILE).each { line ->
    

     readFile(env.SERVICE_VERSIONS_FILE).split("\n").each { line ->
    

    它有效。

    编辑 正如@zett42 在他们的评论中提到的,这显然是设计使然,在处理文件时您应该只使用构建步骤(readFile、writeFile)。 (https://issues.jenkins-ci.org/browse/JENKINS-37577?focusedCommentId=267445&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-267445)

    【讨论】:

    • 根本原因在 comment at this Jira issue 上进行了解释:“行为是“按设计的”。低级 Java 命令将始终访问 master 上的文件。有管道命令,如 readFile () 和 writeFile(),它们允许访问节点工作区中的文件。"
    • 嗨@zett42 谢谢你,我认为你是对的,这可以解释。随意发表您的评论作为答案,我会接受它,否则我会更新我的“答案”以引用您的评论。
    • 我认为我们不需要另一个答案,这个答案包含了现在需要的所有信息。干杯!
    • file not found 的错误信息真的很误导人,浪费了很多人的时间。
    【解决方案2】:

    确认 service_versions.csv 处于源代码控制中并首先检出到 Jenkins 工作区。

    Jenkins 中的主从机制和将作业绑定到从属的操作应确保将工作空间复制到从属。您应该在文件系统上的位置下看到一个工作区,以配置从站来存储文件。您应该能够在 Manage Jenkins > Manage Nodes 页面上找到它,然后查看节点的属性。

    如果您的文件是 .NET 解决方案的一部分,并且文件属性未设置为始终复制,您也可以看到此类问题。

    根据您的评论更新:

    您的 cat 行在 env 前面包含一个“$”字符,但下一行没有它:

    new File(env.SERVICE_VERSIONS_FILE).each { line ->
    

    看起来您在这一行的 env 前面缺少一个“$”,它应该是:

    new File(${env.SERVICE_VERSIONS_FILE}).each { line ->
    

    【讨论】:

    • 我检查了,文件在那里。这也是我在管道中包含 sh "cat..." 命令的原因。在尝试读取之前验证文件是否存在。
    【解决方案3】:

    我通过提供我的 Jenkinsfile 的自定义路径解决了这个错误。

    【讨论】:

      最近更新 更多