在Jenkins流水线脚本中找不到文件

6
我是一名能够翻译文字的助手。

我试图让当前在我们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?

4个回答

10
我不太确定它是如何或为什么起作用的,但我找到了这个链接:https://dev59.com/5mAh5IYBdhLWcg3wBPXY#38679858,并通过使用 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)


根本原因在Jira问题的评论中有解释:“这是“按设计”行为。低级Java命令将始终访问主机上的文件。有一些Pipeline命令,如readFile()和writeFile(),允许访问节点工作区内的文件。” - zett42
嗨@zett42,谢谢你,我认为你是对的,那可以解释这个问题。请随意将您的评论发布为答案,我会接受它,否则我会更新我的“答案”以引用您的评论。 - vruum
我认为我们不需要另一个答案,这个答案现在已经包含了所有所需的信息。干杯! - zett42
文件未找到的错误信息真的非常误导人,导致人们浪费了很多时间。 - herm

0

确认 service_versions.csv 文件已在源代码控制中,并首先将其检出到 Jenkins 工作区。

Jenkins 中的主从机制和将作业绑定到从机应确保工作区被复制到从机。您应该在文件系统下的位置上看到一个工作区,以配置从机来存储文件。您可以在“管理 Jenkins”>“管理节点”页面上找到此选项,然后查看节点的属性。

如果您的文件是 .NET 解决方案的一部分,并且文件属性未设置为“始终复制”,则还可能会出现此类问题。

根据您的评论更新:

您的 cat 行在 env 前面包含 '$' 字符,而下一行没有:

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

看起来你在这一行的 env 前面漏了一个 '$',应该改为:

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

我已经检查过,文件在那里。这也是为什么我在管道中包含了 sh "cat..." 命令的原因。为了在尝试读取文件之前验证文件是否存在。 - vruum

0

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


0
  1. 从您的位置打开文本文件 -> 点击文件 -> 另存为。

  2. 将名称更改为 .java 扩展名 (例如: sample.java)。

  3. 它将创建一个新的Java文件。

  4. 运行Jenkins脚本一次。

如果您在配置中提供javac sample.java和Java示例,则它将起作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接