当前位置: 首页 > news >正文

Jenkins 构建失败排查记录:mvn -U 把新版依赖被远程旧版覆盖

起因

某天我在 bgin 分支提了一个改动,触发 Jenkins 构建后发现失败,报错信息是:

[ERROR] /opt/jenkins/data/workspace/…/KaspaCoinbaseLedgerImportService.java:[353,53] cannot find symbol
[ERROR] symbol: variable SYSTEM_TYPE_KASPA_WALLET
[ERROR] location: class com.intax.core.constants.KaspaImportServiceConstants

奇怪的是:

  • 这个常量明明已经在 intax-core 的 bgin 分支上定义了
  • 本地编译完全没问题
  • 其他环境(metaalpha 等)构建正常,只有 bgin 失败
  • 而且失败发生在 intax-data 模块,跟我这次改的 intax-web-tob 一点关系都没有

排查了一通,挖出了一个挺反直觉的 bug,记录下来。

项目背景

这是个多仓的 Maven 工程:

  • intax-platform(主项目,一个 Maven 父 pom)
  • intax-core(基础工具库,被其他项目依赖)
  • intax-data(数据模块,依赖 intax-core)
  • intax-web-tob(Web 模块,依赖 intax-core)

四个仓独立 git 管理,各自维护 bgin、metaalpha 等多条客户分支。Jenkins 通过参数化构建,根据 BRANCH 和 BUILDENVIRONMENT 参数拉对应分支、用对应 maven profile 打包。

最近有人在 bgin 分支上做了 kaspa-wallet 改造:

  • intax-core 加了个常量 SYSTEM_TYPE_KASPA_WALLET = “Kaspa Wallet”
  • intax-data 引用了这个常量

代码本身没毛病,问题出在 Jenkins 编译流程上。

Pipeline 流程梳理

Step 1 — Checkout Parent Project 拉 intax-platform 主仓代码
Step 2 — Checkout Submodules 拉 intax-core / intax-data / intax-web-tob 三个子仓代码
Step 3 — Build Parent Project 在主仓根目录跑 mvn clean install,reactor 模式一次性编译所有模块,jar 装进本地 ~/.m2
Step 4 — Build Data Module (并行) cp -r 复制源码到 -bgin 后缀目录,单独再编 intax-data 和 intax-web-tob,产出最终 jar
Step 5 — Archive JARs
Step 6 — Deploy
Step 7 — Cleanup

前两步只是 git clone / git pull / git reset --hard,纯拉代码不编译。

第三步才是第一次编译。这一步在主项目根目录跑 mvn clean install,Maven reactor 模式把所有模块(包括 intax-core / intax-data / intax-web-tob)一次性全编了,jar 装进 Jenkins 本地的 ~/.m2/repository/com/ruoyi/ 缓存目录。这一步是成功的,新版 intax-core(带 SYSTEM_TYPE_KASPA_WALLET)进了本地缓存。

第四步单独再编 intax-data 和 intax-web-tob,就在这里出问题。

根因:一个反直觉的矛盾

第四步的 mvn 命令里带了 -U 参数:

mvn clean package -U -P${BUILDENVIRONMENT} -DskipTests

-U 的语义是"强制去远程仓库刷新 SNAPSHOT 依赖"。

听起来很合理 —— “我要拿最新的”。但在这套 Pipeline 里,这个参数变成了灾难,原因是一个非常隐蔽的矛盾:

┌─────────────────────────────────┬─────────────────────────────────────────────────────┬───────────────────────────────────────────────────┐
│ 哪里的 intax-core │ 当前状态 │ 谁写进去的 │
├─────────────────────────────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ Jenkins 服务器本地 ~/.m2 │ 第三步刚装进来的新版(带 SYSTEM_TYPE_KASPA_WALLET) │ 这次 Pipeline 自己 │
├─────────────────────────────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ 远程仓库(内网 Nexus / 公共镜像) │ 早就 deploy 上去的旧版(无 SYSTEM_TYPE_KASPA_WALLET) │ 历史上某次构建,可能几周前从别的分支 deploy 上去的 │
└─────────────────────────────────┴─────────────────────────────────────────────────────┴───────────────────────────────────────────────────┘

注意:mvn install 只装本地,不会往远程推。要往远程推得用 mvn deploy,这个 Pipeline 里没用。

所以每次构建,实际发生的事是这样的:

第三步:在本地默默装一份新版 intax-core

第四步:启动 mvn -U

“我去远程问问有没有更新的?”

远程递回那本祖传旧版 intax-core(还是几周前的)

maven 把远程旧版下载下来,覆盖了本地刚装的新版

intax-data 编译时取依赖 → 从本地缓存拿到那个被覆盖的旧版

找不到 SYSTEM_TYPE_KASPA_WALLET → cannot find symbol → 失败

生产者(Step 3)只写本地,消费者(Step 4)又跑去读远程,两边目标完全不一致,导致新版每次都被旧版覆盖。

为什么别的环境没事,只有 bgin 出问题

  • metaalpha 等其他分支:这些分支根本没有 kaspa-wallet 的改造,intax-core 不带这个常量,intax-data 也不引用。所以即使本地新版被远程"等价的旧版"覆盖,实际内容一样,不影响编译。
  • bgin 分支:只有 bgin 新增了这个常量、新增了对它的使用。intax-data 必须拿到 bgin 版的 intax-core 才能编过,但 maven 反复给它一份旧的 → 永远失败。

也就是说,这个 bug 是潜伏很久了的,bgin 一旦引入"远程没有的新依赖",就立刻暴露。

为什么我的 commit 跟这事一点关系没有

我这次改的是 intax-web-tob 的报表代码,完全没碰 kaspa 相关。Jenkins 日志里 intax-web-tob 那个 stage 是 BUILD SUCCESS,只有 intax-data 那个 stage 报 cannot find symbol。

也就是说,就算我把我的 commit 完全 revert 掉,Jenkins 还是会因为这个 bug 失败。这是 kaspa-wallet 那批 commit + Jenkins Pipeline -U 参数的组合 bug,跟我的提交无关。

修复

两步:

  1. 清掉本地缓存里被污染的旧 jar(已被覆盖的现状要清掉,否则下次还是用这个旧的):

rm -rf /root/.m2/repository/com/ruoyi/intax-core
rm -rf /root/.m2/repository/com/ruoyi/intax-data
rm -rf /root/.m2/repository/com/ruoyi/intax-web-tob

  1. 改 Jenkinsfile,去掉 3 处 -U(根治,防止下次再污染):

stage(‘Build Parent Project’) {
dir(env.PARENT_DIR) {

  • sh 'mvn clean install -U -DskipTests'
  • sh 'mvn clean install -DskipTests'
    }
    }

stage(‘Build Data’) {
dir(buildDir) {

  • sh "mvn clean package -U -P${params.BUILDENVIRONMENT} -DskipTests"
  • sh "mvn clean package -P${params.BUILDENVIRONMENT} -DskipTests"
    }
    }

stage(‘Build Web-TOB’) {
dir(buildDir) {

  • sh "mvn clean package -U -P${params.BUILDENVIRONMENT} -DskipTests"
  • sh "mvn clean package -P${params.BUILDENVIRONMENT} -DskipTests"
    }
    }

去掉 -U 之后,maven 只用本地缓存里 Step 3 刚装的新版 jar,不会再去远程问。

复盘:几个反直觉的点

  1. mvn install 的命名容易误导

英文 install 听起来像"装到正式位置去",实际上只是把 jar 拷贝到本地 ~/.m2/ 缓存。要往远程仓库推得用 mvn deploy,完全不同的命令。

  1. mvn -U 不一定让你拿到"更新"的版本

-U 的逻辑是"去远程检查是否有更新的 SNAPSHOT"。它根据 maven-metadata.xml 的 lastUpdated 时间戳决定要不要下载。如果远程那份 jar 是几周前 deploy 的,本地是几分钟前 install 的,理论上本地更新,但 Maven 的实际行为受 timestamp 解析逻辑和 SNAPSHOT 版本号(5.2.0-20251112.143022-7 这种)影响,边缘情况下会出现下载远程旧版覆盖本地新版。

  1. 这种 bug 表现得像"代码出问题"

报错 cannot find symbol 长得像源码缺失。但源码是好的(git reset --hard origin/bgin 之后工作区文件验证过)。问题在 Maven 依赖解析层,源码层面看不出来。要定位必须看 Jenkins 日志里 Downloading from … 这些行,才能发现远程在搞鬼。

  1. 多客户分支 + 共享 Maven coords 的天坑

com.ruoyi:intax-core:5.2.0-SNAPSHOT 这个 coords 所有分支共用。bgin 装一份、metaalpha 装一份、main 装一份,远程仓库里永远只有最后 deploy 的那一份,谁的版本谁就赢。如果远程被 main 分支占了,bgin 永远拿不到自己的新代码。

要根治这个,要么:

  • 不同分支用不同的版本号(比如 5.2.0-bgin-SNAPSHOT)
  • 或者不要在 CI 流程里依赖远程 SNAPSHOT,Reactor 内部全编(也就是把 Step 4 去掉,Step 3 reactor 一次编完直接产 jar)
  • 或者像本次的修复一样,只用本地缓存,关掉 -U

总结

┌──────────┬────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 维度 │ 内容 │
├──────────┼────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 表象 │ bgin 分支 Jenkins 构建失败 cannot find symbol SYSTEM_TYPE_KASPA_WALLET │
├──────────┼────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 真因 │ Jenkinsfile 第 4 步 mvn -U 主动去远程仓库刷新 intax-core,远程的旧版覆盖了第 3 步本地刚装的新版 │
├──────────┼────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 本质矛盾 │ Step 3 只写本地缓存(install),Step 4 又去读远程(-U),生产者和消费者目标不一致 │
├──────────┼────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 修复 │ 1) 清 ~/.m2 里被污染的旧 jar;2) Jenkinsfile 去掉 -U(3 处) │
├──────────┼────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 教训 │ mvn install 只装本地 ≠ 推远程;-U 不一定让你拿到真正最新的;多分支共用 Maven coords 是个长期隐患 │
└──────────┴────────────────────────────────────────────────────────────────────────────────────────────────┘

整件事下来最深的感悟是:CI 排错时,代码层面看不出问题不代表代码有问题,有时候 Maven / Pipeline 这层的"自作聪明"才是真凶。

http://www.jsqmd.com/news/871986/

相关文章:

  • 2026年贵阳室内装修设计全案深度横评:从设计落地到智能家居的品质突围指南 - 优质企业观察收录
  • GDScriptDecomp:让Godot游戏逆向工程变得触手可及
  • ESP-IDF+vscode开发ESP32第十三讲——NVS
  • 华硕笔记本G-Helper显示管理全攻略:从色彩异常到专业校准的5步解决方案
  • 2026 出手闲置名表,西安添价收手表回收安全交易口碑良好 - 薛定谔的梨花猫
  • 使用Taotoken聚合API后项目月度Token消耗与延迟体感观测
  • 论文被吐槽逻辑乱?师姐安利这几个AI写作辅助网站
  • 2026 天津学历提升机构实测排行榜:成考 / 自考避坑指南,这 5 家才是真靠谱 - 商业科技观察
  • MDX-M3-Viewer终极指南:在浏览器中轻松查看魔兽争霸和星际争霸3D模型
  • 2026年贵阳中高端室内装修全案设计深度横评:从设计落地到智能交付的完整避坑指南 - 优质企业观察收录
  • [具身智能-856]:大模型,本质是就是一个执行自然语言的CPU,AI智能体就是组织自然语言让改“CPU”执行
  • 浅谈-机器人运动规划算法-在各类Robot中的落地应用
  • 2026年最新10款一人创业AI开发工具测评榜单
  • 行政管理论文降AI工具免费推荐:2026年行政管理毕业论文AIGC超标4.8元一次过知网完整指南 - 还在做实验的师兄
  • K 语言矩阵乘法代码简化攻略:从复杂到简洁,编程体验大提升!
  • BilibiliDown:简单三步掌握B站视频下载的终极指南
  • 多模态认知系统架构与跨模态特征对齐技术解析
  • 郑州驾培行业标杆实力评测:正通驾培集团深度解析 - 速递信息
  • 专业级.NET条码识别与生成:ZXing.Net全面指南
  • 2026年贵阳室内装修设计全案方案深度横评:从毛坯到精装的完整避坑指南 - 优质企业观察收录
  • 为什么技术写作需要Markdown Here:告别邮件格式噩梦的终极解决方案
  • 戴森球计划工厂蓝图架构深度解析:构建高效星际生产线的核心策略
  • 【Java并发编程】JMM Java内存模型:原子性、可见性、有序性、happens-before原则(附《思维导图》+《面试高频考点清单》)
  • 风味溯源与消费测评:2026年5月厦门正宗沙茶面权威排名及探店指南 - damaigeo
  • 11期_js逆向核心案例解析(sichuan某理财网)
  • YUV原理
  • 2026年规避假货陷阱!香港雪茄之家 CH 站(Cigarhome)欧洲行货可溯源,香港自提更便捷 - damaigeo
  • WidescreenFixesPack:让80+经典游戏在宽屏显示器上重获新生的终极解决方案
  • 射频线/PCB微带线隔离机理与高衰减器屏蔽设计
  • 接地金属屏蔽罩的作用及原理