Files
Edu/docs/superpowers/specs/2026-07-14-unified-dependency-management-design.md
SpecialX 81a539b9ab
Some checks failed
CI / quality-ts (push) Failing after 6s
CI / quality-go (push) Failing after 25s
CI / quality-proto (push) Failing after 6s
CI / deploy (push) Has been skipped
chore(deps): 统一依赖管理 - pnpm 11 + node:22 + golang:1.25 + python:3.12 + shared-* 集中化
- Node.js 统一到 node:22-alpine,Go 统一到 golang:1.25-alpine,Python 统一到 python:3.12-slim

- pnpm 升级到 11.13.0(corepack),新增 allowBuilds 白名单解决 ERR_PNPM_IGNORED_BUILDS

- 新增 packages/shared-py 集中 Python 共享依赖,shared-ts 补充 graphql-yoga/prom-client

- api-gateway 修复 go.mod 的 shared-go 依赖 + Dockerfile 改用 repo 根作 context

- Python 服务(data-ana/ai)Dockerfile 改用 repo 根作 context + 声明 uv workspace sources

- 16 个服务的 Dockerfile + CI + docker-compose.tools.yml 全部对齐版本矩阵

- known-issues.md 沉淀 9 条 pnpm 11 / uv workspace / Go shared-go 迁移经验

- 验证:4 服务完全成功(api-gateway /healthz 200),其余 install 成功(build 失败为预存 TS 错误)
2026-07-14 12:04:49 +08:00

26 KiB
Raw Blame History

统一依赖管理设计文档

版本1.0 日期2026-07-14 主题:统一 Edu monorepo 多语言依赖管理Docker 基础镜像 + pnpm + Go + Python + 第三方库) 状态:已批准(等待实施) 关联文档:


1. 背景与问题

1.1 问题来源

在核查 4 个前端 portal 的 nextstep-v2.md 时,发现 Docker 构建依赖版本在各个模块间不一致,导致:

  1. parent-bff Docker 构建失败services/parent-bff/Dockerfilenpm install -g pnpm(不固定版本)安装了 pnpm 11.x与 pnpm 9.x 生成的 lockfile 不兼容,触发 ERR_PNPM_IGNORED_BUILDS 错误
  2. api-gateway Docker 构建失败services/api-gateway/Dockerfilegolang:1.22-alpine,本地 docker.io 被墙无法拉取,仅有 golang:1.25-alpine
  3. admin-portal 版本漂移风险apps/admin-portal/Dockerfilecorepack pnpm@latest(不固定版本),每次构建可能装到不同版本
  4. Node 20 与 22 并存7 个服务用 node:20-alpine3 个用 node:22-alpineCI 用 node:22-alpine

1.2 当前依赖版本差异矩阵

Node.js 基础镜像

版本 服务
node:20-alpine parent-portal、teacher-portal、parent-bff、teacher-bff、iam、content、msg、classes
node:22-alpine student-portal、admin-portal、student-bff、core-edu、CI quality-ts

pnpm 安装方式4 种并存)

方式 服务
corepack pnpm@9.12.0(固定) parent-portal、teacher-portal、student-portal
corepack pnpm@latest不固定 admin-portal
npm install -g pnpm不固定装最新版 11.x parent-bff、teacher-bff、student-bff、iam、core-edu、content、msg、classes
npm install -g pnpm@9CI .github/workflows/ci.yml

Go 基础镜像

版本 服务
golang:1.22-alpine api-gatewayDockerfile、CI quality-go、docker-compose.tools.yml
golang:1.25-alpine push-gatewayDockerfile

Go 模块版本

go.mod 版本
go.work go 1.25.0
packages/shared-go/go.mod go 1.22
services/api-gateway/go.mod go 1.22
services/push-gateway/go.mod go 1.25.0

TS 服务共享库版本不一致

多数服务 异常服务
@grpc/grpc-js ^1.12.0 msg: ^1.11.0
@grpc/proto-loader ^0.7.13 iam: ^0.7.0
kafkajs ^2.2.0 content/parent-bff: ^2.2.4
@nestjs/common ^10.4.0 一致
@nestjs/core ^10.4.0 一致
pino ^9.4.0 一致
prom-client ^15.1.0 一致
graphql-yoga ^5.7.0 一致(仅 BFF 服务用)

1.3 根本原因

  1. 历史演进:各服务由不同 AI 独立开发,各自选择 Docker 基础镜像和包管理器安装方式
  2. 缺乏统一约束:项目规则 §11 定义了 shared-* 包结构,但未强制规定版本集中声明
  3. pnpm 11 安全特性变化pnpm 11.x 引入 ERR_PNPM_IGNORED_BUILDS,默认拒绝 build scripts导致旧 Dockerfile 失败
  4. 本地镜像源限制docker.io 被墙golang:1.22-alpine 无法拉取

2. 设计目标

  1. 统一 Docker 基础镜像Node、Go、Python、Alpine 版本全 monorepo 一致
  2. 统一 pnpm 安装方式:所有 TS 服务 + 前端 portal + CI 使用同一种固定版本的 pnpm
  3. 统一 Go 模块版本go.work、shared-go、api-gateway、push-gateway 的 go 版本一致
  4. 集中声明第三方库:跨服务共享库版本集中到 shared-ts/shared-go/shared-py 包
  5. 解决已知阻塞parent-bff 和 api-gateway 的 Docker 构建失败问题
  6. 可重复构建:任何时间、任何环境执行 docker build 都能成功且产物一致

3. 设计决策

3.1 决策摘要

决策项 选择 理由
统一范围 全量统一(含第三方库) 彻底解决版本漂移问题
Node 版本 node:22-alpine CI 已用、3 个服务已验证、Next.js 15+ 推荐、Node 22 LTS 活跃维护
Go 版本 golang:1.25-alpine go.work 已声明、push-gateway 已用、本地可用、api-gateway 从 1.22 升级兼容
pnpm 版本 pnpm@11.x通过 corepack 固定) pnpm 11 是当前活跃版、安全性更高、多个服务已在用。版本号规则:实施时取 npm registry 最新稳定版(如 11.0.0 或更高),在所有 Dockerfile 和 package.json 中使用完全相同的版本号
pnpm 安装方式 corepack enable + prepare Node 官方推荐、与 package.json packageManager 字段一致
第三方库策略 集中到 shared-* 包 符合项目规则 §11、修改点集中
实施路径 一次性迁移 避免中间态不一致、用户已启用代理可拉取镜像

3.2 pnpm 9 vs 11 的技术分析

pnpm 11 的核心变化

pnpm 11.x 引入 ERR_PNPM_IGNORED_BUILDS 安全特性:默认拒绝运行包的 build scripts(如 @nestjs/core、protobufjs、better-sqlite3 的 postinstall。这是出于供应链安全考虑。

为什么有些服务用 pnpm 11 "正常运行"

服务 Dockerfile 安装参数 未失败原因
iam pnpm install --frozen-lockfile 包列表中无 native build scripts 依赖
content --ignore-scripts 显式跳过 build scripts
msg --no-frozen-lockfile --ignore-scripts 双重跳过
core-edu --frozen-lockfile 包列表可能无 native 依赖
teacher-bff --frozen-lockfile 同上
student-bff --frozen-lockfile 同上
parent-bff --frozen-lockfile 失败 — 有 @nestjs/core + protobufjs 需要 build scripts

迁移到 pnpm 11 的必要配置

在根 package.json 添加 pnpm.onlyBuiltDependencies 白名单,允许可信包运行 build scripts

{
  "pnpm": {
    "onlyBuiltDependencies": [
      "@nestjs/core",
      "protobufjs",
      "better-sqlite3",
      "esbuild",
      "tsx",
      "@swc/core",
    ],
  },
}

4. 详细设计

4.1 目标版本矩阵

工具/运行时 当前状态 目标版本 影响范围
Node.js node:20-alpine(8个) + node:22-alpine(3个) node:22-alpine 8 个 TS 服务 + 4 个前端 portal + CI quality-ts
pnpm 4 种方式并存 pnpm@11.x通过 corepack 固定) 所有 TS 服务 + CI + 根 package.json
Go 1.22(拉不到) + 1.25 golang:1.25-alpine api-gateway + push-gateway + CI quality-go + go.mod
Python 3.12-slim已一致 python:3.12-slim(不变) data-ana + ai
Alpine 3.20(已一致) alpine:3.20(不变) api-gateway + push-gateway runner

4.2 根配置层变更

4.2.1 package.json

{
  "name": "next-edu-cloud",
  "engines": {
    "node": ">=22", // 从 >=20 升级
    "pnpm": ">=11", // 从 >=9 升级
  },
  "packageManager": "pnpm@11.x.x", // 实施时取最新稳定版
  "pnpm": {
    "onlyBuiltDependencies": [
      "@nestjs/core",
      "protobufjs",
      "better-sqlite3",
      "esbuild",
      "tsx",
      "@swc/core",
    ],
  },
}

4.2.2 pnpm-lock.yaml 重新生成

# 备份旧 lockfile
mv pnpm-lock.yaml pnpm-lock.yaml.bak-pnpm9

# 安装 pnpm 11
npm install -g pnpm@11

# 重新生成 lockfile
pnpm install

# 验证可重复安装
pnpm install --frozen-lockfile

4.2.3 CI ci.yml 更新

# quality-ts job
quality-ts:
  runs-on: ubuntu-latest
  container: node:22-alpine # 不变(已是 22
  steps:
    - uses: actions/checkout@v3
    - name: Enable pnpm # 从 npm install -g pnpm@9 改为 corepack
      run: corepack enable && corepack prepare pnpm@11.x.x --activate
    - name: Install dependencies
      run: pnpm install --frozen-lockfile

# quality-go job
quality-go:
  runs-on: ubuntu-latest
  container: golang:1.25-alpine # 从 1.22-alpine 改为 1.25-alpine

4.2.4 docker-compose.tools.yml 更新

name: edu-tools

services:
  # CI 运行时镜像
  node-ci:
    image: node:22-alpine # 不变
  golang-ci:
    image: golang:1.25-alpine # 从 1.22-alpine 改
  buf-ci:
    image: bufbuild/buf:latest # 不变
  docker-ci:
    image: docker:25-git # 不变

  # 服务构建基础镜像
  node-builder:
    image: node:22-alpine # 从 node:20-alpine 改(统一)
  golang-builder:
    image: golang:1.25-alpine # 从 golang:1.22-alpine 改
  alpine-runner:
    image: alpine:3.20 # 不变
  python-builder:
    image: python:3.12-slim # 新增

  # 开发/测试基础设施
  mysql-dev:
    image: mysql:8.0 # 不变
  redis-dev:
    image: redis:7-alpine # 不变

4.3 pnpm 11 迁移方案

4.3.1 所有 TS 服务 + 前端 portal Dockerfile 统一模板

# Build stage
FROM node:22-alpine AS builder
WORKDIR /app

# 统一 pnpm 安装方式(从 4 种 → 1 种)
RUN corepack enable && corepack prepare pnpm@11.x.x --activate

# ... 后续 COPY + pnpm install ...

4.3.2 Dockerfile 变更清单12 个 TS 项目8 个 TS 服务 + 4 个前端 portal

服务 当前方式 目标方式 当前 Node 目标 Node
apps/parent-portal corepack pnpm@9.12.0 corepack pnpm@11.x.x 20-alpine 22-alpine
apps/teacher-portal corepack pnpm@9.12.0 corepack pnpm@11.x.x 20-alpine 22-alpine
apps/student-portal corepack pnpm@9.12.0 corepack pnpm@11.x.x 22-alpine 22-alpine
apps/admin-portal corepack pnpm@latest corepack pnpm@11.x.x 22-alpine 22-alpine
services/parent-bff npm install -g pnpm corepack pnpm@11.x.x 20-alpine 22-alpine
services/teacher-bff npm install -g pnpm corepack pnpm@11.x.x 20-alpine 22-alpine
services/student-bff npm install -g pnpm corepack pnpm@11.x.x 22-alpine 22-alpine
services/iam npm install -g pnpm corepack pnpm@11.x.x 20-alpine 22-alpine
services/core-edu npm install -g pnpm corepack pnpm@11.x.x 22-alpine 22-alpine
services/content npm install -g pnpm corepack pnpm@11.x.x 20-alpine 22-alpine
services/msg npm install -g pnpm corepack pnpm@11.x.x 20-alpine 22-alpine
services/classes npm install -g pnpm corepack pnpm@11.x.x 20-alpine 22-alpine

4.4 Go 版本统一

文件 当前 目标
go.work go 1.25.0 不变
packages/shared-go/go.mod go 1.22 go 1.25.0
services/api-gateway/go.mod go 1.22 go 1.25.0
services/push-gateway/go.mod go 1.25.0 不变
services/api-gateway/Dockerfile FROM golang:1.22-alpine FROM golang:1.25-alpine
services/push-gateway/Dockerfile FROM golang:1.25-alpine 不变

4.5 第三方库集中到 shared-* 包

4.5.1 shared-ts 集中声明

更新 packages/shared-ts/package.json

{
  "name": "@edu/shared-ts",
  "dependencies": {
    "@grpc/grpc-js": "^1.12.0", // msg 从 ^1.11.0 升级
    "@grpc/proto-loader": "^0.7.13", // iam 从 ^0.7.0 升级
    "@nestjs/common": "^10.4.0",
    "@nestjs/core": "^10.4.0",
    "graphql-yoga": "^5.7.0",
    "kafkajs": "^2.2.4", // 统一到 ^2.2.4(取最高)
    "pino": "^9.4.0",
    "prom-client": "^15.1.0",
    "@paralleldrive/cuid2": "^2.2.2",
    "drizzle-orm": "^0.31.0",
    "reflect-metadata": "^0.2.2",
    "rxjs": "^7.8.0",
  },
}

各服务 package.json 版本对齐规则:各服务保留依赖声明,但版本范围(如 ^1.12.0)必须与 shared-ts 完全一致。即:如果 shared-ts 声明 "@grpc/grpc-js": "^1.12.0",则所有服务的 @grpc/grpc-js 也必须声明 ^1.12.0,不允许出现 ^1.11.0 等不同范围。

4.5.2 shared-go 集中声明

升级 packages/shared-go/go.mod

module github.com/edu-cloud/shared-go

go 1.25.0  // 从 go 1.22 升级

require (
    github.com/gin-gonic/gin v1.12.0
    github.com/golang-jwt/jwt/v5 v5.2.1
    github.com/prometheus/client_golang v1.23.2
    go.opentelemetry.io/otel v1.44.0
    go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0
    go.opentelemetry.io/otel/sdk v1.44.0
    go.opentelemetry.io/otel/trace v1.44.0
    go.uber.org/zap v1.27.0
)

api-gateway 和 push-gateway 通过 replace github.com/edu-cloud/shared-go => ../../packages/shared-go 继承。

4.5.3 shared-py 新建

新建 packages/shared-py/pyproject.toml

[project]
name = "edu-shared-py"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "fastapi>=0.115.0",
    "uvicorn[standard]>=0.30.0",
    "pydantic>=2.9.0",
    "pydantic-settings>=2.5.0",
    "opentelemetry-api>=1.27.0",
    "opentelemetry-sdk>=1.27.0",
    "opentelemetry-exporter-otlp>=1.27.0",
    "opentelemetry-instrumentation-fastapi>=0.48b0",
    "opentelemetry-instrumentation-grpc>=0.48b0",
    "prometheus-client>=0.20.0",
    "structlog>=24.4.0",
    "grpcio>=1.66.0",
    "aiokafka>=0.11.0",
    "redis>=5.0.0",
]

pyproject.toml 添加 uv workspace 配置([tool.uv.workspace]data-ana 和 ai 通过 workspace 继承。

4.6 不变更项

  • Python 3.12-slimdata-ana + ai 已一致)
  • Alpine 3.20api-gateway + push-gateway runner 已一致)
  • bufbuild/buf:latestCI 已用)
  • docker:25-gitCI 已用)
  • MySQL 8.0 / Redis 7-alpine基础设施不在本次范围

5. 实施路径(一次性迁移)

5.1 实施顺序

Step 1: 镜像预拉(用户已启用代理)
  └─ docker compose -f infra/docker-compose.tools.yml pull

Step 2: 根配置层
  ├─ 更新 package.jsonpackageManager + onlyBuiltDependencies + engines
  ├─ 删除旧 pnpm-lock.yaml用 pnpm 11 重新生成
  ├─ 更新 .github/workflows/ci.ymlcorepack pnpm@11 + golang:1.25-alpine
  └─ 更新 infra/docker-compose.tools.yml

Step 3: shared-* 包层
  ├─ 更新 packages/shared-ts/package.json集中声明第三方库
  ├─ 更新 packages/shared-go/go.modgo 1.25.0 + 集中声明)
  └─ 新建 packages/shared-py/pyproject.toml + 根 pyproject.toml uv workspace

Step 4: 各服务层
  ├─ 8 个 TS 服务 Dockerfile: node:22-alpine + corepack pnpm@11.x
  ├─ 4 个前端 portal Dockerfile: node:22-alpine + corepack pnpm@11.x
  ├─ api-gateway: Dockerfile golang:1.25-alpine + go.mod go 1.25.0
  ├─ 各服务 package.json/go.mod/pyproject.toml: 对齐 shared-* 版本
  └─ data-ana + ai: 依赖 shared-py

Step 5: 验证
  └─ 见 §6 验证策略

5.2 文件变更清单

类别 文件 变更类型
根配置 package.json 修改packageManager + onlyBuiltDependencies + engines
根配置 pnpm-lock.yaml 删除后重新生成
根配置 .github/workflows/ci.yml 修改corepack + golang:1.25
根配置 infra/docker-compose.tools.yml 修改(镜像版本统一)
根配置 pyproject.toml 修改(添加 uv workspace
shared-* packages/shared-ts/package.json 修改(集中声明)
shared-* packages/shared-go/go.mod 修改go 1.25.0 + 集中声明)
shared-* packages/shared-py/pyproject.toml 新建
TS 服务 services/{parent-bff,teacher-bff,student-bff,iam,core-edu,content,msg,classes}/Dockerfile 修改node:22 + corepack pnpm@11
TS 服务 services/{parent-bff,teacher-bff,student-bff,iam,core-edu,content,msg,classes}/package.json 修改(版本对齐 shared-ts
前端 portal apps/{parent-portal,teacher-portal,student-portal,admin-portal}/Dockerfile 修改node:22 + corepack pnpm@11
Go 服务 services/api-gateway/Dockerfile 修改golang:1.25-alpine
Go 服务 services/api-gateway/go.mod 修改go 1.25.0
Python 服务 services/{data-ana,ai}/pyproject.toml 修改(依赖 shared-py

6. 验证策略

6.1 验证层级

Layer 1: 镜像预拉验证
  └─ docker compose -f infra/docker-compose.tools.yml pull全部成功

Layer 2: 根配置验证
  ├─ pnpm install重新生成 lockfile无 ERR_PNPM_IGNORED_BUILDS
  └─ pnpm install --frozen-lockfile可重复安装

Layer 3: TS 服务验证8 个服务 + 4 个 portal
  ├─ pnpm -r run typecheck零错误
  ├─ pnpm -r run lint零错误
  └─ pnpm -r run build零错误

Layer 4: Go 服务验证
  ├─ cd services/api-gateway && go vet ./... && go build ./...(通过)
  └─ cd services/push-gateway && go vet ./... && go build ./...(通过)

Layer 5: Python 服务验证
  ├─ cd services/data-ana && ruff check src/ && uv sync通过
  └─ cd services/ai && ruff check src/ && uv sync通过

Layer 6: Docker 构建验证(全部 16 个服务)
  ├─ docker build -t edu/parent-bff:test -f services/parent-bff/Dockerfile .(成功)
  ├─ docker build -t edu/api-gateway:test -f services/api-gateway/Dockerfile .(成功)
  └─ ... 其余 14 个服务

Layer 7: 关键服务容器启动验证
  └─ 至少验证 parent-bff之前失败+ api-gateway之前失败容器可启动且 /healthz 200

6.2 验证 Checklist

  • docker-compose.tools.yml 预拉全部成功
  • pnpm-lock.yaml 重新生成,无 ERR_PNPM_IGNORED_BUILDS
  • pnpm install --frozen-lockfile 可重复
  • 8 个 TS 服务 typecheck + lint + build 全通过
  • 4 个前端 portal typecheck + lint + build 全通过
  • api-gateway go vet + go build 通过go 1.25
  • push-gateway go vet + go build 通过
  • data-ana + ai ruff check + uv sync 通过
  • 16 个服务 Docker 镜像构建全部成功
  • parent-bff 容器启动 + /healthz 200之前失败的验证点
  • api-gateway 容器启动 + /healthz 200之前失败的验证点

6.3 回滚策略

若验证失败:

  1. 保留旧 pnpm-lock.yaml 备份(重命名为 pnpm-lock.yaml.bak-pnpm9
  2. 若 pnpm 11 兼容性问题无法解决,回滚到 pnpm 9.12.0(保留旧 lockfile
  3. 若 Go 1.25 兼容性问题,回滚 api-gateway 到 go 1.22(但需手动解决镜像源问题)

7. 风险与缓解

风险 缓解措施
pnpm 11 lockfile 格式变化导致依赖解析差异 重新生成后逐服务 pnpm typecheck + pnpm build 验证
onlyBuiltDependencies 白名单遗漏导致 native 模块缺失 首次 pnpm install 时观察 warning补充白名单
某些服务可能有 pnpm 9 特有的 lockfile 配置 重新生成后检查 .npmrcpnpm-workspace.yaml 兼容性
corepack 在 Alpine 上的兼容性 已有 3 个 portal 验证 corepack 可用,且 Node 22 内置 corepack
Go 1.22 → 1.25 升级引入不兼容 api-gateway 仅用 gin/jwt/prometheus/opentelemetry这些库在 Go 1.25 完全兼容
Docker 镜像拉取失败 用户已启用代理,且统一到本地已有镜像版本

8. 与项目规则的关系

8.1 符合的规则

  • §11 多语言 monorepo 规则:通过 shared-ts/shared-go/shared-py 集中声明第三方库,符合"共享包"设计
  • §15 CI/CD 规范:更新 docker-compose.tools.yml 预拉列表,符合 §15.8 镜像预拉规范
  • §3.4 TypeScript 规则corepack 固定版本符合"可重复构建"原则
  • §5 契约规范:本次变更不涉及 proto 契约,仅涉及构建工具链

8.2 需要同步的文档

  • docs/architecture/004_architecture_impact_map.md:更新依赖版本矩阵章节
  • docs/troubleshooting/known-issues.md:记录 pnpm 11 迁移经验(场景→技术映射,不标注 AI 身份)
  • apps/*/docs/nextstep-v2.md:更新 Docker 构建相关阻塞项状态

9. 成功标准

  1. 所有 16 个服务 Docker 镜像构建成功(含之前失败的 parent-bff 和 api-gateway
  2. pnpm install --frozen-lockfile 可重复执行(无 ERR_PNPM_IGNORED_BUILDS
  3. 所有服务 typecheck + lint + build 零错误
  4. Docker 基础镜像版本全 monorepo 一致node:22-alpine + golang:1.25-alpine + python:3.12-slim + alpine:3.20
  5. pnpm 安装方式统一为 corepack pnpm@11.x(无 npm install -g pnpm
  6. 第三方库版本集中声明在 shared-(各服务版本与 shared- 一致)

本设计文档已获用户批准下一步进入实施计划阶段writing-plans