fix(ci): 修复三处必致 CI 变红的工程缺陷

排查 Phase 23 收尾项「 GitHub Actions 从未跑绿」时,逐个在本地复刻 CI 的每个 job,
发现三个与业务代码无关、但足以让流水线恒红的缺陷。

1. 依赖锁文件被排除出版本控制(admin-web job 必红)
   .gitignore 原先同时忽略 pnpm-lock.yaml 与 package-lock.json,导致
   CI checkout 后 admin-web 根本拿不到 lockfile。pnpm install --frozen-lockfile
   在缺 lockfile 时不是降级重试、而是直接报错,因此该 job 必然失败;
   miniapp 的 npm install 也因失去锁文件而版本浮动。
   现在按包管理器逐一放行:admin-web 走 pnpm、miniapp 走 npm。
   admin-web/package-lock.json 仍保持忽略 —— 同一目录并存两把锁会让两种包管理器
   各按自己的锁安装,漂移时彼此看不出问题。

2. admin-web/pnpm-workspace.yaml 内容残缺(任何环境下都装不上依赖)
   该文件原先只有两行 allowBuilds(esbuild / vue-demi),既无 packages 声明也未写取值。
   pnpm 一旦检测到工作目录下存在 pnpm-workspace.yaml 就强制进入 workspace 模式,
   随后因读不到 packages 而报 packages field missing or empty。
   本仓库 admin-web 只有一个包,现显式声明单包 workspace,并补出 esbuild 与
   vue-demi 的 build 白名单 —— 二者 postinstall 是必需步骤:
   esbuild 下载平台原生二进制、vue-demi 生成 Vue 版本桥接。

3. CI 使用 runner 预装的 pnpm,版本会漂移
   pnpm 9 与 10 对「是否默认放行依赖 build 脚本」的语义相反,而上述
   allowBuilds / onlyBuiltDependencies 恰好是版本敏感配置项。 runner 镜像升级后
   会出现 install 能过、build 变红、且错误信息不指向真正原因的情况。
   现钉住 pnpm@9.15.9,使 CI 语义不再随 runner 变化。

本轮同时在本地复刻了 CI 的其余 job:
- migration job 的迁移序列校验:51 个迁移文件,版本 1 至 51 连续且唯一,通过。
- test-shell-guard:18 个 Python 文件未发现 skip / pass 空壳,通过。
- miniapp job 的 npm install 加 tsc --noEmit:通过。
This commit is contained in:
WorkBuddy
2026-09-22 09:40:44 +08:00
parent 8bd1ab804a
commit c6b48d5d91
5 changed files with 2266 additions and 3 deletions
+8
View File
@@ -45,6 +45,14 @@ jobs:
cache: 'pnpm' cache: 'pnpm'
cache-dependency-path: admin-web/pnpm-lock.yaml cache-dependency-path: admin-web/pnpm-lock.yaml
# 必须钉版本:runner 预装的 pnpm 会随 GitHub 镜像升级而漂移,而本仓库的
# pnpm-workspace.yaml 含有 onlyBuiltDependencies 等版本敏感配置,
# 且 pnpm 9 与 10 对「是否默认放行依赖 build 脚本」的语义相反
# (esbuild / vue-demi 的 postinstall 若被静默阻断,install 能过、build 会红,
# 且错误信息不指向真正原因)。钉死后 CI 语义不再随 runner 变化。
- name: 锁定 pnpm 版本(消除 runner 预装版本漂移)
run: npm i -g pnpm@9.15.9
- name: Install Dependencies - name: Install Dependencies
working-directory: admin-web working-directory: admin-web
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile
+17
View File
@@ -10,6 +10,23 @@ package-lock.json
yarn.lock yarn.lock
pnpm-lock.yaml pnpm-lock.yaml
# 例外:依赖锁文件必须入库,CI 的可复现安装依赖它们。
#
# 2026-09-22 修复背景:上面四条规则此前把 admin-web/pnpm-lock.yaml 与
# miniapp/package-lock.json 一并挡在版本控制之外,于是 CI checkout 后的环境
# 与本机完全不同:
# - admin-web:`pnpm install --frozen-lockfile` 找不到 lockfile → 直接失败
# (frozen 模式的语义就是「必须存在且不可变更」,缺文件不是警告而是错误);
# - miniapp:`npm install` 无 lockfile 约束,版本随上游浮动,
# `npx tsc --noEmit` 的结果不与任何一次本地验证对齐。
# 二者共同导致「本地绿、CI 红」且二者都不可解释。
#
# 只放行每个包真正使用的那把锁:admin-web 走 pnpm,miniapp 走 npm。
# admin-web/package-lock.json 仍然忽略 —— 同一目录并存两把锁会让两种包管理器
# 各自按自己的锁安装,漂移时彼此看不出问题,属于典型的双锁冲突。
!admin-web/pnpm-lock.yaml
!miniapp/package-lock.json
# Build outputs # Build outputs
backend/target/ backend/target/
admin-web/dist/ admin-web/dist/
+2180
View File
File diff suppressed because it is too large Load Diff
+24 -3
View File
@@ -1,3 +1,24 @@
allowBuilds: # 本目录只有一个前端包,不是 pnpm workspace。
esbuild: set this to true or false #
vue-demi: set this to true or false # 2026-09-22 修复背景:
# 本文件原先只有两行 allowBuilds(esbuild / vue-demi),既没有 packages 声明,
# 也没写实际取值。pnpm 一旦检测到工作目录下存在 pnpm-workspace.yaml,
# 就会强制进入 workspace 模式,随后因读取不到 packages 而直接报错退出:
#
# ERROR packages field missing or empty
#
# 结果是 admin-web 的 `pnpm install --frozen-lockfile` 在任何环境都装不上依赖
# (本地与 CI 表现一致),表现为 admin-web job 秒红,且与业务代码无关。
#
# allowBuilds 是 pnpm 10 才引入的配置项;pnpm 9 不认识它。这意味着
# 「这个残缺文件是否被接受」取决于 runner 上的 pnpm 版本 —— 典型的版本漂移陷阱。
# 因此这里显式声明单包 workspace,并在 CI 中锁死 pnpm 版本(见 .github/workflows/ci.yml),
# 让「workspace 模式」与「只读 RFC2119 白名单」两条路径都不再受 runner 升级影响。
#
# esbuild 与 vue-demi 的 postinstall 是必需步骤(前者下载平台原生二进制,
# 后者生成 Vue 版本桥接),允许它们执行。
packages:
- '.'
onlyBuiltDependencies:
- esbuild
- vue-demi
+37
View File
@@ -0,0 +1,37 @@
{
"name": "danyang-match-miniapp",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "danyang-match-miniapp",
"version": "1.0.0",
"devDependencies": {
"miniprogram-api-typings": "^5.2.3",
"typescript": "^5.3.3"
}
},
"node_modules/miniprogram-api-typings": {
"version": "5.2.3",
"resolved": "https://registry.npmmirror.com/miniprogram-api-typings/-/miniprogram-api-typings-5.2.3.tgz",
"integrity": "sha512-KVcNSfR2VmRtz35rxkjLxE92HlsBnbT6NsKyTcDX3ebbTKAsAiTOFO1mRM82Xbe848dnEYqNGzaD48LMEvGytw==",
"dev": true,
"license": "MIT"
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
}
}
}