fix(infra): pnpm 工具链去脆弱化 —— frontend/package.json 落 packageManager pin + deploy.sh pnpm 版本断言(fail-closed)
ci/woodpecker/push/main Pipeline failed
ci/woodpecker/push/main Pipeline failed
① frontend/package.json 补 "packageManager": "pnpm@10.33.2+sha512.a90faf6f…": corepack 在项目未声明该字段时按全局 lastKnownGood.json 解析版本,而该文件会被任何跑过 corepack 的项目改写(2026-10-13 → 12.11.2 ⇒ MODULE_NOT_FOUND;2026-10-14 → 9.15.9 ⇒ packages field missing)。pin 落库后 corepack 只认 pin,宿主机状态漂移不再影响部署树。 选 10.33.2 的依据:pnpm-workspace.yaml 的 allowBuilds 仅 pnpm 10 识别;部署树 node_modules .modules.yaml packageManager=pnpm@10.33.2(store/v11/v10);lockfileVersion 9.0;2026-10-13 22:53 该版本下部署成功。 ② deploy.sh 新增 assert_pnpm_version()(fail-closed,两处调用:1.2a 首次 pnpm 调用前、[2] 构建前): 期望版本只取自 package.json 的 packageManager(单一源),与 pnpm -v 实读逐字比对,缺失/不一致 即中止;两次调用都先于 [3] 铺站与 [5] 重启 ⇒ 任何生产写之前失败关闭。 零生产写:候选仅落本 worktree 分支,未 push、未动 /srv/hermes/shared/cma-deploy 与 dev 树。
This commit is contained in:
committed by
Hermes CI Fix
parent
e3e6b3ba8a
commit
32f368a846
@@ -36,6 +36,34 @@ UNIT_FILE="${CMA_DEPLOY_UNIT_FILE:-/etc/systemd/system/cma-backend.service}"
|
|||||||
ENV_SRC="$REPO_DIR/backend/.env"
|
ENV_SRC="$REPO_DIR/backend/.env"
|
||||||
VENV_SRC="$REPO_DIR/backend/venv"
|
VENV_SRC="$REPO_DIR/backend/venv"
|
||||||
|
|
||||||
|
# ── pnpm 版本断言(2026-10-14 deploy-chain-rootfix;fail-closed)────────────────
|
||||||
|
# 背景:corepack 在项目**未声明** `packageManager` 时,按全局
|
||||||
|
# /root/.cache/node/corepack/lastKnownGood.json 解析 pnpm 版本 —— 该文件会被**任何**
|
||||||
|
# 跑过 corepack 的项目改写(本型事故历史 ≥3 次:2026-10-13 12.11.2 ⇒ `Cannot find
|
||||||
|
# module '…/bin/pnpm.cjs'`;2026-10-14 9.15.9 ⇒ `packages field missing or empty`)。
|
||||||
|
# ⇒ 无 pin 的部署树会**静默换工具链**,一路跑到 pnpm build 才炸,且修复只能落在宿主机状态上。
|
||||||
|
# 做法:期望版本**只**取自 `frontend/package.json` 的 `packageManager`(单一源),与
|
||||||
|
# `pnpm -v` 实读逐字比对(也覆盖 COREPACK_ENABLE_PROJECT_SPEC=0 等让 pin 失效的形态);
|
||||||
|
# 缺失或不一致即中止。断言先于任何 pnpm 调用与任何生产写([3] 铺站 / [5] 重启)。
|
||||||
|
# 用法:在部署树 frontend 目录内调用 `assert_pnpm_version`。
|
||||||
|
assert_pnpm_version() {
|
||||||
|
local expect actual
|
||||||
|
expect="$(sed -n 's/.*"packageManager"[[:space:]]*:[[:space:]]*"pnpm@\([^"+]*\).*/\1/p' package.json 2>/dev/null | head -1)"
|
||||||
|
if [ -z "$expect" ]; then
|
||||||
|
echo "❌ pnpm 版本断言失败:frontend/package.json 未声明 \"packageManager\": \"pnpm@<ver>+sha512…\""
|
||||||
|
echo " 无 pin ⇒ corepack 按全局 lastKnownGood 解析 ⇒ 工具链不可复现 ⇒ 中止(生产未被触碰)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
actual="$(pnpm -v 2>/dev/null | tail -1 | tr -d '[:space:]' || true)"
|
||||||
|
if [ "$actual" != "$expect" ]; then
|
||||||
|
echo "❌ pnpm 版本断言失败:package.json pin=$expect,实读 pnpm -v=${actual:-<空>}"
|
||||||
|
echo " 多为 corepack lastKnownGood 漂移(/root/.cache/node/corepack/lastKnownGood.json)"
|
||||||
|
echo " 修法:HOME=/root corepack prepare pnpm@$expect --activate | 中止(生产未被触碰)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ✓ pnpm 版本断言通过:pnpm $actual == package.json pin $expect"
|
||||||
|
}
|
||||||
|
|
||||||
echo "===== 管理会计OS 生产部署 ====="
|
echo "===== 管理会计OS 生产部署 ====="
|
||||||
echo " 入口工作区(开发区): $REPO_DIR"
|
echo " 入口工作区(开发区): $REPO_DIR"
|
||||||
echo " 部署树(已提交树) : $DEPLOY_SRC"
|
echo " 部署树(已提交树) : $DEPLOY_SRC"
|
||||||
@@ -138,6 +166,10 @@ mkdir -p "$DEPLOY_SRC/backend/logs" "$DEPLOY_SRC/backend/scripts/backups"
|
|||||||
if [ -f "$REPO_DIR/frontend/.env" ]; then
|
if [ -f "$REPO_DIR/frontend/.env" ]; then
|
||||||
ln -sfn "$REPO_DIR/frontend/.env" "$DEPLOY_SRC/frontend/.env"
|
ln -sfn "$REPO_DIR/frontend/.env" "$DEPLOY_SRC/frontend/.env"
|
||||||
fi
|
fi
|
||||||
|
# 1.2a pnpm 版本断言(fail-closed;先于**首次** pnpm 调用):坏工具链不得先污染部署树 node_modules
|
||||||
|
if [ -d "$DEPLOY_SRC/frontend" ]; then
|
||||||
|
( cd "$DEPLOY_SRC/frontend" && assert_pnpm_version )
|
||||||
|
fi
|
||||||
if [ ! -e "$DEPLOY_SRC/frontend/node_modules" ]; then
|
if [ ! -e "$DEPLOY_SRC/frontend/node_modules" ]; then
|
||||||
echo " ℹ️ 部署树缺 frontend/node_modules → 尝试 pnpm install --frozen-lockfile --prefer-offline"
|
echo " ℹ️ 部署树缺 frontend/node_modules → 尝试 pnpm install --frozen-lockfile --prefer-offline"
|
||||||
if ! ( cd "$DEPLOY_SRC/frontend" && pnpm install --frozen-lockfile --prefer-offline ); then
|
if ! ( cd "$DEPLOY_SRC/frontend" && pnpm install --frozen-lockfile --prefer-offline ); then
|
||||||
@@ -171,6 +203,9 @@ echo " ✓ 鉴权面审计通过(无 B 类缺口)"
|
|||||||
# 2. 构建前端(在部署树内构建:pnpm/corepack 的生成物落在这里,污染不到开发区)
|
# 2. 构建前端(在部署树内构建:pnpm/corepack 的生成物落在这里,污染不到开发区)
|
||||||
echo "[2/5] 构建前端..."
|
echo "[2/5] 构建前端..."
|
||||||
cd "$DEPLOY_SRC/frontend"
|
cd "$DEPLOY_SRC/frontend"
|
||||||
|
# [2] 段前 pnpm 版本断言(fail-closed):与 package.json 的 packageManager pin 不一致即中止,
|
||||||
|
# 先于 [3] 铺站与 [5] 重启 ⇒ 任何生产写之前失败关闭。
|
||||||
|
assert_pnpm_version
|
||||||
pnpm build
|
pnpm build
|
||||||
echo " ✓ 构建完成"
|
echo " ✓ 构建完成"
|
||||||
# 构建工具链(corepack/pnpm、unplugin-auto-import/vue-components)会在部署树里写生成物 ——
|
# 构建工具链(corepack/pnpm、unplugin-auto-import/vue-components)会在部署树里写生成物 ——
|
||||||
|
|||||||
@@ -51,5 +51,6 @@
|
|||||||
"unplugin-vue-components": "^32.0.0",
|
"unplugin-vue-components": "^32.0.0",
|
||||||
"vite": "^5.0.0",
|
"vite": "^5.0.0",
|
||||||
"vue-tsc": "^3.2.7"
|
"vue-tsc": "^3.2.7"
|
||||||
}
|
},
|
||||||
|
"packageManager": "pnpm@10.33.2+sha512.a90faf6feeab71ad6c6e57f94e0fe1a12f5dcc22cd754db40ae9593eb6a3e0b6b12e3540218bb37ae083404b1f2ce6db2a4121e979829b4aff94b99f49da1cf8"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user