Jenkins Vue 前端流水线构建异常排查(pnpm 版本、husky、构建脚本拦截、OOM)

Jenkins Vue 前端流水线构建异常排查(pnpm 版本、husky、构建脚本拦截、OOM)

记录时间:2026-07-16
环境:Jenkins(K8s 动态 Slave,devops 命名空间)/ 共享库 pipelinePublicLibrary / node 容器 gzeport-node-pnpm:20|22 / 项目 gzeport-track-ui-xc(Vue3 + TS + Vite7,4353 模块)

一、问题现象

新接入的 Vue 前端项目 gzeport-track-ui-xc 首次走 vueDevopsByk8s 流水线,📦 前端构建 (pnpm) 阶段连续失败 4 次,每次报错各不相同:

  1. pnpm 启动即崩:Error [ERR_UNKNOWN_BUILTIN_MODULE]: No such built-in module: node:sqlite
  2. pnpm install 尾部报 git command not found,后续构建命令未执行
  3. pnpm install 尾部报 ERR_PNPM_IGNORED_BUILDS: Ignored build scripts: esbuild@0.25.5, vue-demi@0.14.10 ...
  4. vite build 进行到 rendering chunks... 时输出 Killedexit code 137

同域其他 Vue 项目(如 gzeport-report-ui-xc)用相同流水线构建正常。

二、排查过程

2.1 问题 1:pnpm 与 Node 版本不匹配

对比正常项目与异常项目的构建日志头部:

# 正常项目(gzeport-report-ui-xc)
node: v20.19.5    pnpm: 10.22.0

# 异常项目(gzeport-track-ui-xc)
warn: This version of pnpm requires at least Node.js v22.13
warn: The current version of Node.js is v20.19.5
Error [ERR_UNKNOWN_BUILTIN_MODULE]: No such built-in module: node:sqlite

根因:项目 package.json 绑定了 packageManager: pnpm@11.8.0,容器内 pnpm 自举拉起 11.8.0,而该版本依赖 Node 22 才有的内置模块 node:sqlite。构建参数 nodeVersion 默认 20,撑不起 pnpm 11。

注意:其他项目未绑定 pnpm 版本,直接使用容器自带的 pnpm 10.22.0,与 Node 20 兼容,所以只有该项目触发。

处理:构建参数 nodeVersion 改选 22

2.2 问题 2:husky postinstall 中断 pnpm install

nodeVersion=22 后,pnpm install 包全部安装成功,但尾部:

$ husky
git command not found

Jenkins sh 块默认 set -e,husky(prepare 脚本)非零退出导致整个 sh 块中断,pnpm run build:uat 根本没执行。根因:node 构建容器内没有 git 命令,husky 初始化 git hooks 失败。

处理:业务 Jenkinsfile 注入 HUSKY=0(流水线的 stageEnvPrepare 会执行 map.each { k,v -> env."${k}" = "${v}" },map 字段自动进环境变量):

map.put("HUSKY","0")

husky 官方支持该变量,CI 中输出 HUSKY=0 skip install 并以 0 退出。

2.3 问题 3:pnpm 10+ 拦截依赖构建脚本

husky 跳过后,pnpm install 尾部仍非零退出:

[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: @parcel/watcher@2.5.1, es5-ext@0.10.64, esbuild@0.25.5, typeit@8.8.7, vue-demi@0.14.10

pnpm 10 起默认拦截依赖包的 postinstall 构建脚本(防供应链攻击),且以非零码退出。

尝试 1(无效)pnpm install --config.onlyBuiltDependencies='*' —— * 被当作字面包名而非通配符,反而覆盖了默认允许列表。

尝试 2(撤销)pnpm config set onlyBuiltDependencies[] esbuild 等逐包写入 —— 该改动位于公共共享库,等于把单个项目的依赖清单固化进公共流水线;onlyBuiltDependencies 是排他性白名单,会静默跳过其他项目依赖的构建脚本,产生难排查的运行时问题。

最终方案:使用 pnpm 10.4+ 官方 CI 开关,放行所有构建脚本(等价 pnpm 9 及以前的默认行为):

// vars/vueDevopsByk8s.groovy
pnpm install --registry ${params.registryUrl} --dangerously-allow-all-builds

注意:该标志名称中的 “dangerously” 针对开发者本机场景;CI 容器为一次性隔离环境,构建完即销毁,放行构建脚本是该开关的设计用途。node20 容器 pnpm 10.22.0 与 node22 容器 pnpm 11.8.0 均支持;node24 容器未验证(要求 pnpm ≥ 10.4)。

2.4 问题 4:vite build OOM(exit 137)

依赖安装走通后,构建阶段:

vite v7.1.3 building for uat...
✓ 4353 modules transformed.
rendering chunks...
Killed
[ELIFECYCLE] Command failed with exit code 137.

exit 137 = SIGKILL,容器内存超限被 K8s OOMKill。将 node 容器 limits.memory 从 4Gi 提到 6Gi 后仍然 OOM,说明加内存方向不对。

三、根因分析

检查项目 package.json 构建脚本:

"build:uat": "vue-tsc --noEmit & vite build --mode uat"

关键在单个 &(后台执行),不是 &&(条件与):

  • vue-tsc(全量类型检查,大项目吃 2~3G)被扔到后台,与 vite build(打包,2~3G)并行执行,两进程内存叠加冲破容器 limit;
  • & 后无 wait,vue-tsc 的退出码被丢弃——类型检查失败构建照样成功,即该脚本中的 vue-tsc 在 CI 里本来就无拦截作用,只消耗内存;
  • 脚手架(create-vue)默认模板即如此,多个项目同样写法,小项目内存没顶到 limit 所以未暴露。

结论:继续加内存是为一个无产出的进程买单;正确方向是 CI 构建跳过 vue-tsc。

四、解决方案

共享库构建命令改为 Groovy 层拼装(避免 shell 多层 if 嵌套),支持 SKIP_TYPE_CHECK 开关:

// vars/vueDevopsByk8s.groovy 构建 stage
def pm = params.packageManager
def modeSuffix = params.profiles ? ":${params.profiles}" : ""
def buildCmd = "${pm} run build${modeSuffix}"
// SKIP_TYPE_CHECK=true:绕过 npm script 直接调 vite,跳过无拦截作用的 vue-tsc
if (map.SKIP_TYPE_CHECK == "true") {
    def modeOpt = params.profiles ? " --mode ${params.profiles}" : ""
    buildCmd = "${pm} exec vite build${modeOpt}"
}

业务 Jenkinsfile 声明:

map.put("SKIP_TYPE_CHECK","true")

方案要点:

要点 说明
环境切换不受影响 --mode 仍取 profiles 构建参数(uat / prod-xc),未写死
包管理器不受影响 命令前缀取 packageManager 参数(pnpm / npm)
其他项目零影响 未注入 SKIP_TYPE_CHECK 的项目走原有 run build:xxx 逻辑
无安全隐患 布尔开关而非任意命令注入,不使用 eval

五、验证

# 构建参数:nodeVersion=22, profiles=uat, isDeploy=false
# 预期日志:
# 构建命令: pnpm exec vite build --mode uat
# pnpm install ... Done(含 esbuild 等 postinstall 正常执行)
# HUSKY=0 skip install
# vite v7.1.3 building for uat... ✓ built

截至记录时点:问题 1~3 已验证通过(依赖安装完整走通、构建脚本正常执行);问题 4 的 SKIP_TYPE_CHECK 方案已完成代码修改,待共享库 push 后触发构建做最终验证。

六、注意事项

共享库改动必须 push 到 GitLab main 才生效——Jenkins 每次构建重新拉取 pipelinePublicLibrary@main,本地修改不 push 等于没改。

inline Job 与仓库归档副本需手动同步——Jenkins Job 的 Pipeline script 是运行实体,ops-jenkinsfile 仓库中的 Jenkinsfile 只是归档,改任何一侧都要同步另一侧。

  • 项目绑定 packageManager 版本时,先确认对应 Node 版本要求,再选构建参数 nodeVersion
  • 改公共流水线(共享库)时,禁止固化单一项目的特定依赖清单或构建参数;通用开关 + 项目 Jenkinsfile 按需声明
  • CI 资源类故障(OOM)先分析进程行为再考虑加资源;本例根因是无效并行进程,不是内存不足
  • 若希望类型检查成为真正的 CI 门禁,应推动项目将脚本改为 vue-tsc --noEmit && vite build&& 串行),由前端团队决策;串行后内存不再叠加,但构建时间变长

七、参考资料

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇