Jenkins跳过Checkout阶段时自定义工作区Git轮询失效问题咨询
问题描述
- 基于Jenkins搭建软件编译部署流水线,对接Bitbucket Cloud时使用Bitbucket插件实现代码轮询与流水线自动触发,初始checkout配置如下:
checkout(poll: true, scm:[$class: 'GitSCM', branches: [[name: "my_branch"]], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: "my_dir"]], userRemoteConfigs: [[credentialsId: "$credentialsId", url: "my_repo"]] ])
- 该流水线的Jenkinsfile存放在软件代码仓库外部,在stage层级定义了自定义目录用于代码拉取操作,流水线包含Checkout、Build、Deploy1、Deploy2多个支持跳过配置的阶段。
- 异常表现:流水线全量执行时所有功能运行正常,但如果Checkout阶段被跳过,Git轮询逻辑就会异常:轮询会忽略配置的自定义目录,转而在Jenkins任务默认工作区路径下检测代码变更。
可复现该问题的最小化流水线代码如下:
properties([pipelineTriggers([pollSCM("* * * * *")])]) pipeline { parameters { booleanParam(name: 'UPDATE_SOURCES', defaultValue: True) } stages { stage('Checkout') { agent { label 'agent_name' } when { expression { return params.UPDATE_SOURCES } } steps { dir("some_full_path_dir") { script { checkout(poll: true, scm:[$class: 'GitSCM', branches: [name: "branch_name"], userRemoteConfigs: [[credentialsId: "whatever", url: "repo_url"]] ]) } } } } } }
当Checkout阶段被跳过时,后续构建的轮询逻辑会忽略配置的some_full_path_dir自定义路径,仅在C:\Jenkins\etc\etc这类默认工作区路径下检测代码变更;只有Checkout阶段正常执行时,Git轮询功能才可正常运行。该问题目前无官方解决方案,以下为经过验证的可行变通方案。
变通实现方案
方案1:将SCM轮询配置注册逻辑抽离到不可跳过的全局步骤
Jenkins内置的SCM轮询逻辑依赖每次构建成功执行checkout步骤时记录的SCM元数据(包含仓库地址、分支、目标工作目录等信息),如果带poll: true的checkout步骤所在阶段被跳过,Jenkins就会丢失自定义目录配置,回退到默认工作区路径做检测。
- 不要把带轮询标记的checkout步骤放在带
when判断的可跳过阶段中,将SCM配置定义为全局变量,在流水线最外层、所有业务阶段之前的固定执行块(比如初始化步骤、全局pre步骤)中,执行一次轻量checkout,仅用于让Jenkins正确记录SCM轮询需要的配置信息。 - 轻量checkout可配置浅克隆参数降低开销,执行完可直接清理临时拉取的文件,不会影响后续正式拉代码的逻辑,示例配置:
// 全局定义SCM配置 def scmConfig = [$class: 'GitSCM', branches: [[name: "branch_name"]], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: "some_full_path_dir"]], userRemoteConfigs: [[credentialsId: "whatever", url: "repo_url"]] ] pipeline { agent any triggers { pollSCM('* * * * *') } stages { stage('InitScmPollMeta') { // 该阶段不设置任何when判断,永远执行 steps { script { // 浅克隆拉取,仅用于注册轮询元数据 checkout(poll: true, changelog: false, scm: scmConfig, depth: 1, noTags: true) // 可选:清理临时拉取的文件,不影响后续流程 dir("some_full_path_dir") { deleteDir() } } } } stage('Checkout') { agent { label 'agent_name' } when { expression { return params.UPDATE_SOURCES } } steps { dir("some_full_path_dir") { script { // 正式拉取代码,这里不需要再设置poll:true checkout(scm: scmConfig) } } } } // 其余Build、Deploy阶段保持原有逻辑不变 } }
方案2:弃用定时轮询,改用Webhook主动触发
Jenkins内置的SCM轮询本身存在实时性差、依赖本地工作区状态的问题,生产环境更推荐直接通过Bitbucket Cloud的Webhook触发流水线:
- 移除流水线中配置的
pollSCM触发器,在Bitbucket仓库的Webhook配置中,添加代码推送事件对应的Jenkins触发地址,指定分支有代码提交时Bitbucket会主动发起请求触发构建,完全绕开Jenkins本地的目录轮询逻辑,不会再出现路径不匹配的问题。 - 触发时可在Webhook中携带最新commit信息、提交人等参数,流水线直接根据参数判断是否需要执行代码拉取、后续构建部署步骤,稳定性远高于定时轮询。
方案3:自定义代码变更检测逻辑
如果必须保留轮询能力,可以完全弃用Jenkins内置的pollSCM逻辑,自行实现固定目录下的变更检测:
- 移除内置
pollSCM触发器,在流水线不可跳过的初始化阶段,直接进入自定义代码目录执行Git命令对比commit哈希判断是否有新变更,根据检测结果设置标记位,再决定是否执行后续的Checkout、Build等阶段。核心检测逻辑示例:
def hasCodeChange = false dir("some_full_path_dir") { if (fileExists(".git")) { def localCommit = sh(script: "git rev-parse HEAD", returnStdout: true).trim() sh "git fetch --depth=1 origin branch_name" def remoteCommit = sh(script: "git rev-parse FETCH_HEAD", returnStdout: true).trim() hasCodeChange = localCommit != remoteCommit } else { // 目录不存在时判定需要拉取代码 hasCodeChange = true } } // 将hasCodeChange存入全局变量,供后续阶段的when判断使用
内容的提问来源于stack exchange,提问作者IonutS
相关产品推荐
相关产品推荐

