使用多Git仓库时Jenkins出现NoSuchFileException问题求助
问题描述
我在使用Jenkins从仓库加载文件时,持续遇到java.nio.file.NoSuchFileException错误。当前使用两个Git仓库:
- repo1.git:包含
test.jenkinsfile、initialemail.groovy、statusemail.groovy文件 - repo2.git:包含构建/部署阶段的所有文件
Jenkins流水线配置为:从SCM加载流水线脚本,SCM选择Git,仓库地址git@gitlab.com:test/repo1.git,分支testbranch,脚本路径jenkins/test.jenkinsfile。
test.jenkinsfile内容如下:
//create an empty map to capture the stage status def stageStatusMap = [:] pipeline { agent any environment { AWS_CREDENTIALS = credentials('Test') } stages { stage('Send Initial Email') { steps { script { def initialemailsender = load 'jenkins/initialemail.groovy' initialemailsender.sendemail() } } } stage('Checkout') { steps { git branch: 'master', credentialsId: 'test', url: 'git@gitlab.com:test/repo2.git' } } stage('Build') { steps { //some build steps here from the Docker files in repo2.git } post { success { script { stageStatusMap['Build'] = 'Success' } } failure { script { stageStatusMap['Build'] = 'Failed' } } } } } post { always { script { //Load the statusemail from repo1.git statusemail = load 'jenkins/statusemail.groovy' statusemail.sendemail(stageStatusMap) echo "test" } cleanWs() echo 'Cleaned up the workspace and finished executing pipeline!' } } }
执行流水线时触发错误:
java.nio.file.NoSuchFileException: /data/jenkins/workspace/Test/jenkins/statusemail.groovy
原因:检出repo2后,工作区被替换为repo2的内容,找不到来自repo1的statusemail.groovy文件。重新检出repo1可以解决,但希望找到无需重复拉取的方案。
已尝试的无效方法:
- 在流水线开头定义加载
statusemail.groovy,因load必须在阶段内执行而失败; - 在发送初始邮件阶段加载,该变量无法在post块中访问。
求无需重新检出repo1即可在post块中加载statusemail.groovy的解决方案。
解决方案
方法1:提前加载脚本并存储为全局变量
在检出repo2之前,提前加载repo1中的脚本并赋值给全局变量(不要加def限制作用域),后续即使工作区被替换,已加载的脚本对象仍可直接调用。
修改后的Jenkinsfile示例:
// 全局变量,存储加载的邮件脚本对象 def initialemailsender def statusemailsender def stageStatusMap = [:] pipeline { agent any environment { AWS_CREDENTIALS = credentials('Test') } stages { stage('Load Repo1 Scripts') { steps { script { // 此时工作区仍为repo1内容,加载两个邮件脚本 initialemailsender = load 'jenkins/initialemail.groovy' statusemailsender = load 'jenkins/statusemail.groovy' } } } stage('Send Initial Email') { steps { script { initialemailsender.sendemail() } } } stage('Checkout Repo2') { steps { git branch: 'master', credentialsId: 'test', url: 'git@gitlab.com:test/repo2.git' } } stage('Build') { steps { //some build steps here from the Docker files in repo2.git } post { success { script { stageStatusMap['Build'] = 'Success' } } failure { script { stageStatusMap['Build'] = 'Failed' } } } } } post { always { script { // 直接调用已加载的全局变量 statusemailsender.sendemail(stageStatusMap) echo "test" } cleanWs() echo 'Cleaned up the workspace and finished executing pipeline!' } } }
方法2:将repo2检出到独立子目录
使用dir步骤限定repo2的检出目录,避免覆盖repo1的文件,确保repo1的脚本始终存在于工作区根目录。
修改后的Checkout阶段及构建阶段:
stage('Checkout Repo2') { steps { dir('repo2-workspace') { // 指定repo2检出到子目录 git branch: 'master', credentialsId: 'test', url: 'git@gitlab.com:test/repo2.git' } } } stage('Build') { steps { dir('repo2-workspace') { // 在repo2子目录下执行构建操作 //some build steps here from the Docker files in repo2.git } } post { success { script { stageStatusMap['Build'] = 'Success' } } failure { script { stageStatusMap['Build'] = 'Failed' } } } }
此时post块中可以直接加载jenkins/statusemail.groovy,因为根目录的repo1文件未被覆盖。
方法3:迁移脚本到Jenkins共享库
将邮件脚本封装为Jenkins共享库,彻底摆脱工作区文件依赖,这是长期维护流水线脚本的最优方案。
- 构建共享库目录结构:
email-shared-lib/ ├── vars/ │ ├── sendInitialEmail.groovy │ └── sendStatusEmail.groovy └── src/ // 可选:封装更复杂的逻辑到类中
- 在Jenkins全局配置中添加该共享库(路径或Git仓库地址)。
- 修改Jenkinsfile引入共享库并调用方法:
@Library('email-shared-lib') _ // 引入共享库 def stageStatusMap = [:] pipeline { agent any environment { AWS_CREDENTIALS = credentials('Test') } stages { stage('Send Initial Email') { steps { script { sendInitialEmail() // 调用共享库方法 } } } stage('Checkout Repo2') { steps { git branch: 'master', credentialsId: 'test', url: 'git@gitlab.com:test/repo2.git' } } stage('Build') { steps { //some build steps here from the Docker files in repo2.git } post { success { script { stageStatusMap['Build'] = 'Success' } } failure { script { stageStatusMap['Build'] = 'Failed' } } } } } post { always { script { sendStatusEmail(stageStatusMap) // 调用共享库方法 echo "test" } cleanWs() echo 'Cleaned up the workspace and finished executing pipeline!' } } }
内容的提问来源于stack exchange,提问作者tt1997
相关产品推荐
相关产品推荐

