如何在Pipeline中引用Jenkinsfile目录?

44
我有一个Groovy文件,想要从Jenkinsfile中运行。
例如:load script.groovy 然而,如果这个文件和Jenkinsfile存储在同一个目录下,我不确定如何引用它。我是从git加载Jenkinsfile的。我注意到它创建了一个名为workspace@script的文件夹。它没有放置在工作区目录中。我可以硬编码该文件夹,但我不确定规则,并且似乎重新检出代码有点冗余。
java.io.FileNotFoundException: /opt/jenkins_home/jobs/my_job/workspace/script.groovy (No such file or directory)

默认情况下,它从工作区加载,而不是workspace@script

我正在尝试将BuildFlow脚本转换为Pipeline(Workflow)脚本。但我发现,这并不像复制和粘贴那样容易。

Jenkinsfile

node {

//get parameters from Job
def builds = builds.tokenize(",")
def ip_address_node = ip_address_node.trim()
def port_node = port_node.trim()
def branch = branch.trim()
def workspace = pwd()

stage 'Checking out code from esb repository'
git branch: branch, url: 'ssh://git@giturl/integration_bus.git'

load '../workspace@script/esb_deploybar_pipeline/deploy_esb.groovy'

}

deploy_esb.groovy(这是旧的buildflow中的内容,尝试在管道中运行)

import groovy.transform.ToString
import groovy.transform.EqualsAndHashCode
@EqualsAndHashCode
@ToString
class BarDeploy {
    String barFile
    String app
    String integrationServer
}


//parse csv
def csvItemsApps = new HashSet<BarDeploy>();
def csvItemsLibs = new HashSet<BarDeploy>();
def deploymentMapFile = new File(workspace + "/ESB_Deployment_Map.csv")
def isFirstLine = true

stage 'Parsing ESB Deployment CSV'
deploymentMapFile.withReader { reader ->
    while(line = reader.readLine()) {
        if(isFirstLine)
        {
          isFirstLine = false
          continue
        }

        csvLine = line.split(",")
        app = csvLine[0]
        intServer = csvLine[1]

        def barDeploy = new BarDeploy()
        barDeploy.app = app
        barDeploy.integrationServer = intServer
        csvItemsApps.add(barDeploy)


        //get shared libs
        if(csvLine.length > 2 && csvLine[2] != null)
        {
            def sharedLibs = csvLine[2].split(";")
            sharedLibs.each { libString ->
                if(!libString.isAllWhitespace())
                {
                    def lib = new BarDeploy()
                    lib.app = libString
                    lib.integrationServer = intServer
                    csvItemsLibs.add(lib)
                }
            };
        }
    }
};

//get list of bar files to deploy from html and consolidate bar files to deploy with apps in csv 
for (int i = 0; i < builds.size(); i+=3)
{
    if(builds[i].equals("false"))
    {
        //Don't deploy bar if checkbox isn't selected
        continue
    }

    foundInCSV = false

    appToDeploy = builds[i + 1]
    barFileToDeploy = builds[i + 2]

    iterator = csvItemsApps.iterator()
    while (iterator.hasNext())
    {
        barDeploy = iterator.next()
        if(appToDeploy.equalsIgnoreCase(barDeploy.app))
        {
            barDeploy.barFile = barFileToDeploy
            foundInCSV = true
        }
    }

    iterator = csvItemsLibs.iterator()
    while (iterator.hasNext())
    {
        barDeploy = iterator.next()
        if(appToDeploy.equalsIgnoreCase(barDeploy.app))
        {
            barDeploy.barFile = barFileToDeploy
            foundInCSV = true
        }
    }

    if(foundInCSV == false)
    {
        throw new RuntimeException("App: " + appToDeploy + " not found in ESB_Deployment_Map.csv. Please add CSV Entry.")
    }
}


//Do deploy, deploy shared libs first
deployCSVItemsInParallel(ip_address_node,port_node,branch,env_key,csvItemsLibs)
deployCSVItemsInParallel(ip_address_node,port_node,branch,env_key,csvItemsApps)


def deploy(ip_address_node,port_node,branch,deployItem,env_key)
{
    def integrationServer = deployItem.integrationServer
    def app = deployItem.app
    def barFile = deployItem.barFile

    if(barFile == null)
    {
        return;
    }

    println("Triggering Build -> ESB App = " + app +  ", Branch = " 
            + branch + ", Barfile: " + barFile + ", Integration Server = " + integrationServer + ", IP Address: " + ip_address_node 
            + ", Port: " + port_node + ", Env_Key: " + env_key)

    build_closure = { ->
        build("esb_deploybar", 
                      ip_address_node: ip_address_node, port_node: port_node,
                      integrationServer: integrationServer, branch: branch, app: app, barFile: barFile, env_key: env_key)
    }

    return build_closure
}

def deployCSVItemsInParallel(ip_address_node,port_node,branch,env_key,csvItems)
{
    def build_closures = []
    iterator = csvItems.iterator()
    while (iterator.hasNext())
    {
      barDeploy = iterator.next()
      def build_closure = deploy(ip_address_node,port_node,branch,barDeploy,env_key)

      if(build_closure != null)
      {
          build_closures.add(build_closure)
      }
    }

    if(build_closures?.size() > 0)
    {
         parallel(build_closures)
    }
}

请发布您Jenkinsfile的相关部分。 - Mark Chorley
我已经包含了所有使用的源代码。 - CodyK
你应该包含尽可能多的代码来展示你的问题,但是要保持尽可能少的代码,以便让代码易于理解。 - Raúl Salinas-Monteagudo
@CodyK,您是否考虑将我下面的答案标记为正确答案? - Mig82
6个回答

20

我没有看到任何人提到一种情况,即当作业应该在 Jenkins 代理/从属上运行时,如何加载 Groovy 脚本。

由于主节点是从 SCM 检出 Jenkins 管道项目的节点,“Groovy 脚本仅在主节点的文件系统中找到”。因此,尽管以下内容可行:

node {       
    def workspace = pwd() 
    def Bar = load "${workspace}@script/Bar.groovy"
    Bar.doSomething()
}

这只是一个巧合,因为克隆SCM管道的节点与尝试加载其中Groovy脚本的节点相同。然而,只需添加不同代理的名称以在其上执行:

node("agent1"){
    def workspace = pwd() 
    def Bar = load "${workspace}@script/Bar.groovy"
    Bar.doSomething()
}

将会失败,导致:

java.io.IOException: java.io.FileNotFoundException: /Jenkins/workspace/Foo_Job@script/Bar.groovy (No such file or directory)

这是因为这个路径:

/Jenkins/workspace/Foo_Job@script/

该问题仅存在于主Jenkins服务器上,而不存在于运行代理1的服务器上。

因此,如果您面临这个问题,请确保将Groovy脚本加载到全局声明的变量中,以便代理可以使用它们:

def Bar
node {       
    def workspace = pwd() 
    if(isUnix()){
        Bar = load "${workspace}@script/Bar.groovy"
    }
    else{
        Bar = load("..\\workspace@script\\Bar.groovy")
    }
}
node("agent1"){
    Bar.doSomething()
}

注意: 用于在节点之间传递模块的变量必须在节点块之外声明。


3
这里的一个陷阱是,如果主节点上的执行器数量设置为零,则在这种情况下似乎load无法找到您的Groovy文件。 - Harold Putman
你是对的@HaroldPutman。在这种情况下,我唯一看到的前进方式是使用git步骤从node(“agent1”)块内再次克隆脚本存储库(Jenkinsfile和Groovy)。不得不两次克隆脚本(从主机和代理服务器),这是一种浪费,但在不允许在主机上使用执行程序的环境中,您将被迫这样做。 - Mig82

13

如果deploy_esb.groovy文件和Jenkinsfile存储在相同的代码管理库中,您可以执行以下操作:

node {       
   def workspace = pwd() 
   load "${workspace}@script/esb_deploybar_pipeline/deploy_esb.groovy"
}

8
如果任务可以并行执行,则这将失败,因为Jenkins将创建类似于“jobname@2”和“jobname@3”的工作空间,但会重用“jobname@script”。 - kadrach

1
如果这个script.groovy文件与Jenkinsfile一样在项目的根目录下,它将会和Jenkinsfile一起从git获取并存储在同一个文件夹中。因此,您正在使用的命令应该可以正常工作。如果出现错误,请提供更多详细信息。
编辑:现在我可以看到您的Jenkinsfile中检查了一个名为integration_bus的git项目,那里就是groovy脚本所在的位置。您可以像这样指定检出的位置:
checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'esb_deploy']], submoduleCfg: [], userRemoteConfigs: [[url: 'ssh://git@giturl/integration_bus.git']]])

与您所拥有的相反

git branch: branch, url: 'ssh://git@giturl/integration_bus.git'

然后,您应该能够像这样引用位于esb_deploy文件夹中的Groovy脚本。
load 'esb_deploy/esb_deploybar_pipeline/deploy_esb.groovy'

脚本从工作区目录加载,而不是workspace@script,这并不是什么大问题,但我宁愿不将此路径硬编码。 - CodyK
Groovy文件实际上位于另一个仓库中。它位于与Jenkinsfile相同的仓库中。我通过像这样引用文件来使其工作:load '../workspace@script/esb_deploybar_pipeline/deploy_esb.groovy'。我还有其他问题,但那些是不同的问题。谢谢。 - CodyK
这仍然感觉有点不正规,是吗?将您的 Groovy 帮助脚本添加到 Git 存储库中,然后将其作为子模块添加到相关项目中,如何? - sebkraemer

0
您可以假设 Jenkinsfile 中的所有文件操作都相对于当前工作区(在 node 内使用 load 时,这是默认的工作区)。

因此,如果目标文件(例如 deploy_esb.groovy)位于 SCM 中的 foo 文件夹中,则无需额外配置即可正常工作:

git branch: branch, url: 'ssh://git@giturl/integration_bus.git'
load 'foo/deploy_esb.groovy'

如果要加载的文件与 Jenkinsfile 在同一代码库中,可以使用以下命令:

checkout scm
load 'foo/deploy_esb.groovy'

问题是,Groovy文件位于与Jenkinsfile相同的存储库中。忽略那个git clone,那不是它的一部分。我只是复制并粘贴了Jenkins文件。但是我从SCM加载Jenkins文件,所以它会创建一个workspace@script目录。我只是使用这个:'../workspace@script/deploy_esb.groovy'来加载脚本。 - CodyK
然后使用:checkout scm,接着是 load 步骤。 - amuniz
3
我知道,只是检查代码两次似乎有点冗余。 - CodyK

0

这应该可以工作

load "${WORKSPACE}/../${JOB_NAME}@script/esb_deploy/esb_deploybar_pipeline/deploy_esb.groovy"

这对我来说是有效的,但有一个问题:对于使用文件夹的作业,请使用JOB_BASE_NAME而不是JOB_NAME。 - ZHANG Zikai

-1

曾经也遇到同样的问题。最终使用额外的克隆操作,在工作空间目录下获取脚本仓库的副本,以便我可以可靠地访问其中的 Groovy 文件:

dir ('SCRIPTS_DIR') {
    checkout scm
    commonScripts = load 'subdir/Common.groovy' // no def, so script is global
}

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