我有一个Jenkins声明性管道,我一直在Jenkins master上运行,它工作得很好。但是,现在我已经开始尝试在从属节点上执行此操作,管道中调用的groovy脚本无法访问工作区中的文件。
我的文件看起来像这样。。。
pipeline {
agent {
label {
label "windows"
customWorkspace "WS-${env.BRANCH_NAME}"
}
}
stages {
stage('InitialSetup') {
steps {
"${env.WORKSPACE}/JenkinsScripts/myScript.groovy"
}
}
}
我可以在从机上看到它正在创建工作区,从git执行签出,并正确执行脚本。但是,如果脚本中的某些内容尝试与工作区中的文件交互,则会失败。
如果我有像这样简单的东西...
def updateFile(String filename) {
echo env.NODE_NAME
filename = "${env.WORKSPACE}/path/to/file"
def myFile = new File(filename)
<do other things with the file>
}
...它说找不到指定的文件。它给了我它正在寻找的路径,我可以确认文件存在,并且代码在构建主文件时运行。
为什么脚本不能找到这样的文件时,可以只是在主节点上运行?我添加了“回声环境”。NODE_NAME"命令到我的groovy文件,它说脚本正在正确的节点上执行。
谢谢
我已经实现了在从机上自动安装Groovy的代码(用于脚本管道)。也许这个解决方案有点麻烦,但是管道没有提供任何其他方法来实现与旧詹金斯的“执行Groovy脚本”相同的功能,因为管道还不支持插件https://wiki.jenkins.io/display/JENKINS/Groovy插件。
import hudson.tools.InstallSourceProperty;
import hudson.tools.ToolProperty;
import hudson.tools.ToolPropertyDescriptor;
import hudson.tools.ToolDescriptor;
import hudson.tools.ToolInstallation;
import hudson.tools.ToolInstaller;
import hudson.util.DescribableList;
import hudson.plugins.groovy.GroovyInstaller;
import hudson.plugins.groovy.GroovyInstallation;
/*
Installs Groovy on the node.
The idea was taken from: https://devops.lv/2016/12/05/jenkins-groovy-auto-installer/
and https://github.com/jenkinsci/jenkins-scripts/blob/master/scriptler/configMavenAutoInstaller.groovy
COMMENT 1: If we use this code directly (not as a separate method) then we get
java.io.NotSerializableException: hudson.plugins.groovy.GroovyInstaller
COMMENT 2: For some reason inst.getExecutable(channel) returns null. I use inst.forNode(node, null).getExecutable(channel) instead.
TODO: Check if https://jenkinsci.github.io/job-dsl-plugin/#method/javaposse.jobdsl.dsl.helpers.step.MultiJobStepContext.groovyCommand
works better.
*/
@NonCPS
def installGroovyOnSlave(String version) {
if ((version == null) || (version == "")) {
version = "2.4.7" // some default should be
}
/* Set up properties for our new Groovy installation */
def node = Jenkins.getInstance().slaves.find({it.name == env.NODE_NAME})
def proplist = new DescribableList<ToolProperty<?>, ToolPropertyDescriptor>()
def installers = new ArrayList<GroovyInstaller>()
def autoInstaller = new GroovyInstaller(version)
installers.add(autoInstaller)
def InstallSourceProperty isp = new InstallSourceProperty(installers)
proplist.add(isp)
def inst = new GroovyInstallation("Groovy", "", proplist)
/* Download and install */
autoInstaller.performInstallation(inst, node, null)
/* Define and add our Groovy installation to Jenkins */
def descriptor = Jenkins.getInstance().getDescriptor("hudson.plugins.groovy.Groovy")
descriptor.setInstallations(inst)
descriptor.save()
/* Output the current Groovy installation's path, to verify that it is ready for use */
def groovyInstPath = getGroovyExecutable(version)
println("Groovy " + version + " is installed in the node " + node.getDisplayName())
}
/* Returns the groovy executable path on the current node
If version is specified tries to find the specified version of groovy,
otherwise returns the first groovy installation that was found.
*/
@NonCPS
def getGroovyExecutable(String version=null) {
def node = Jenkins.getInstance().slaves.find({it.name == env.NODE_NAME})
def channel = node.getComputer().getChannel()
for (ToolInstallation tInstallation : Jenkins.getInstance().getDescriptor("hudson.plugins.groovy.Groovy").getInstallations()) {
if (tInstallation instanceof GroovyInstallation) {
if ((version == null) || (version == "")) {
// any version is appropriate for us
return tInstallation.forNode(node, null).getExecutable(channel)
}
// otherwise check for version
for (ToolProperty prop in tInstallation.getProperties()) {
if (prop instanceof InstallSourceProperty) {
for (ToolInstaller tInstaller: prop.installers) {
if (
(tInstaller instanceof GroovyInstaller) &&
(tInstaller.id.equals(version))
)
return tInstallation.forNode(node, null).getExecutable(channel)
}
}
}
}
}
return null
}
/* Wrapper function. Returns the groovy executable path as getGroovyExecutable()
but additionally tries to install if the groovy installation was not found.
*/
def getGroovy(String version=null) {
def installedGroovy = getGroovyExecutable(version)
if (installedGroovy != null) {
return installedGroovy
} else {
installGroovyOnSlave(version)
}
return getGroovyExecutable(version)
}
只需将这3个方法放到您的管道脚本中,您就可以借助getGroovy()方法获得Groovy可执行路径。如果尚未安装,则将自动完成安装。您可以使用简单的管道测试此代码,如下所示:
// Main
parallel(
'Unix' : {
node ('build-unix') {
sh(getGroovy() + ' --version')
}
},
'Windows' : {
node ('build-win') {
bat(getGroovy() + ' --version')
}
}
)
对我来说,结果是:
[build-unix] Groovy Version: 2.4.7 JVM: 1.8.0_222 Vendor: Private Build OS: Linux
[build-win] Groovy Version: 2.4.7 JVM: 11.0.1 Vendor: Oracle Corporation OS: Windows 10
我最近也遇到了同样的问题。我有一个python文件,它运行并将结果写入JSON文件。我试图访问JSON文件以从中检索数据。下面是我在声明性管道的stage块中使用的代码:
script {
def jsonSlurper = new JsonSlurper()
def fileParsed = new File("parameters.json")
def dataJSON = jsonSlurper.parse(fileParsed)
}
正如大家已经说过的,上面的FileNotFoundException失败了,因为脚本{}
中的任何内容都将只在主机上运行,而不会在代理上运行。为了解决这个问题,我使用了管道实用程序步骤插件(参考:https://plugins.jenkins.io/pipeline-utility-steps/--如何使用:https://www.jenkins.io/doc/pipeline/steps/pipeline-utility-steps/#writejson-write-json-to-a-file-in-the-workspace)该插件允许您对多种文件格式执行任何读/写操作。
下面是我在安装插件后使用的代码html" target="_blank">示例:
script {
def props = readJSON file: 'parameters.json'
println("just read it..")
println(props)
}
注意:我使用的是jenkins 2.249。1.
事实证明,Groovy文件命令被认为是不安全的,尽管它们将在主机上运行,但不会在从机上运行。如果您从将代理设置为另一个节点的脚本调用它们,它仍然会很好地执行命令,只在主节点上执行,而不是在代理上执行。这是一篇文章的摘录https://support.cloudbees.com/hc/en-us/articles/230922508-Pipeline-Files-manipulation
File类的操作在master上运行,因此只有在build在master上运行时才起作用,在本例中,我创建了一个文件,并检查是否可以使用exists方法在节点上访问它,它不存在,因为新文件(File)
是在master上执行的,要检查这一点,我搜索存在于我的主控上但不在节点中的文件夹用户。
stage 'file move wrong way'
//it only works on master
node('slave') {
def ws = pwd()
def context = ws + "/testArtifact"
def file = ws + '/file'
sh 'touch ' + file
sh 'ls ' + ws
echo 'File on node : ' + new File(file).exists()
echo 'Users : ' + new File('/Users').exists()
sh 'mv ' + file + ' ' + context
sh 'ls ' + ws
}
要执行文件操作命令,建议使用本机命令。
这是一个简单的shell操作示例
stage 'Create file'
sh 'touch test.txt'
stage 'download file'
def out='$(pwd)/download/maven.tgz'
sh 'mkdir -p ./download'
sh 'curl -L http://ftp.cixug.es/apache/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz -o ' + out
stage 'move/rename'
def newName = 'mvn.tgz'
sh 'mkdir -p $(pwd)/other'
sh 'mv ' + out + ' ' + newName
sh 'cp ' + newName + ' ' + out
}
问题内容: 我有一个在Jenkins主服务器上运行过的Jenkins声明式管道,并且运行良好。但是,既然我已经尝试在从属节点上执行此操作,则在管道中调用的常规脚本无法访问工作空间中的文件。 我的jenkinsfile看起来像这样… 我可以在从站上看到它正在创建工作区,从git进行检出并正确执行脚本。但是,如果脚本中的某些内容尝试与工作空间中的文件进行交互,它将失败。 如果我有这样简单的事情… …它
问题内容: 我正在尝试将旧样式的基于项目的工作流转换为基于Jenkins的管道。在浏览文档时,我发现有两种不同的语法分别命名为和。例如最近(2016年底)发布的Jenkins网络语法。尽管有一个新的语法版本,Jenkins仍然也支持脚本语法。 现在,我不确定这两种类型的哪种情况最合适。语法将很快被弃用吗?詹金斯管道的未来会是这样吗? 任何可以分享有关这两种语法类型的想法的人。 问题答案: 最初创建
当我在jenkins中运行多分支管道代码时,我收到以下的以下错误: java.lang.NoSuchMethodError:在步骤中找不到这样的DSL方法“管道”[archive,bat,build,catchError,checkout,deleteDir,dir,echo,emailext,EmailExtrecients,error,fileExists,getContext,git,inp
我查看了以下文档:https://docs.sonarqube.org/display/scan/analysy+with+sonarqube+scanner+for+jenkins 然而,我不能让它工作。首先,文档似乎需要更新,因为示例中的语法是错误的。在最新版本的声明性管道中,在-tag中是必需的。此外,-关键字只有在-标记中才会解析。 null
问题内容: 我的Jenkins 2.19.4使用管道:声明式代理程序API 1.0.1。如果您无法定义变量来分配读取的属性,那么如何使用readProperties? 例如,要捕获SVN版本号,我目前以脚本样式使用以下代码捕获它: 然后我可以使用: 由于以声明式定义svnProp是不合法的,因此如何使用readProperties? 问题答案: 您可以使用标记内的步骤来运行任意管道代码。 所以符合
我写了一个Jenkins管道,相关部分如下所示: 和我的DockerFile: