From 7506372abdedccb4b0a65051adec97a46cbed4b1 Mon Sep 17 00:00:00 2001 From: zyc <1439655764@qq.com> Date: Wed, 13 May 2026 14:13:37 +0800 Subject: [PATCH] fix(ci): hoisted node_modules + alpine binary target for Prisma in Docker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause (from build log): 1. Prisma 6 generates client into @prisma/client package dir (not .prisma/client) 2. pnpm default isolated linker puts everything in .pnpm/ store with symlinks at top-level — Docker COPY of @prisma followed broken/incomplete symlinks 3. node:22-alpine needs linux-musl-openssl-3.0.x engine binary Fixes: - .npmrc: node-linker=hoisted → flat node_modules, COPY behaves like npm - schema.prisma: add linux-musl-openssl-3.0.x to binaryTargets - Dockerfile: drop dead .prisma/client checks, copy only @prisma (where Prisma 6 actually writes the client) plus standalone output Co-Authored-By: Claude Opus 4.7 (1M context) --- .npmrc | 3 +++ Dockerfile | 20 +++++++++++--------- prisma/schema.prisma | 5 ++++- 3 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..1c09209 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +# 让 pnpm 用扁平 node_modules,避免 .pnpm/ 软链结构导致 Docker COPY 断裂 +node-linker=hoisted +auto-install-peers=true diff --git a/Dockerfile b/Dockerfile index 6af62e4..4e66181 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,14 +7,16 @@ WORKDIR /app RUN corepack enable && corepack prepare pnpm@latest --activate \ && pnpm config set registry https://registry.npmmirror.com -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +# .npmrc 必须先 COPY,否则 pnpm install 看不到 node-linker=hoisted +COPY .npmrc package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY prisma ./prisma -# pnpm 10+ 在 root/CI 环境默认不跑 lifecycle scripts,因此显式调用 prisma generate +# pnpm 10+ 在 root/CI 默认跳过 lifecycle scripts,因此显式调用 prisma generate +# Prisma 6 直接把 client 写入 @prisma/client 包目录(不再用 .prisma/client) RUN pnpm install --frozen-lockfile --ignore-scripts \ && pnpm exec prisma generate \ - && ls -la /app/node_modules/.prisma/client/ \ - && ls -la /app/node_modules/@prisma/client/ + && ls -la /app/node_modules/@prisma/client/ \ + && ls /app/node_modules/@prisma/client/ | grep -E "(libquery_engine|schema.prisma|index.js)" || true # ───────────── 2. builder:Next.js 构建(standalone 产物) ───────────── FROM node:22-alpine AS builder @@ -27,10 +29,11 @@ COPY --from=deps /app/node_modules ./node_modules COPY . . ENV NEXT_TELEMETRY_DISABLED=1 -# 再次保险生成(COPY . . 会覆盖 prisma/ schema 的最新版本),然后构建 +# COPY . . 会覆盖 prisma/schema 的最新版本,需要再 generate 一次确保 client 同步 RUN pnpm exec prisma generate \ && pnpm exec next build \ - && ls -la /app/node_modules/.prisma/client/ + && ls -la /app/node_modules/@prisma/client/ \ + && ls -la /app/.next/standalone/ # ───────────── 3. runner:最小运行时镜像 ───────────── FROM node:22-alpine AS runner @@ -45,13 +48,12 @@ ENV HOSTNAME=0.0.0.0 RUN addgroup --system --gid 1001 nodejs \ && adduser --system --uid 1001 nextjs -# Next.js standalone 输出 + 静态资源 + public +# Next.js standalone 自带通过 tracing 解析出的运行时依赖(含 @prisma/client) COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static -# Prisma:生成的 client + schema(运行时 db:push / 迁移可能用到) -COPY --from=builder --chown=nextjs:nodejs /app/node_modules/.prisma ./node_modules/.prisma +# 显式补 Prisma:tracing 有时会漏掉 engine 二进制和 schema COPY --from=builder --chown=nextjs:nodejs /app/node_modules/@prisma ./node_modules/@prisma COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4cc5548..44d5988 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -4,7 +4,10 @@ // ============================================================= generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" + // native:本地开发(macOS / linux-glibc) + // linux-musl-openssl-3.0.x:容器运行时(node:22-alpine) + binaryTargets = ["native", "linux-musl-openssl-3.0.x"] } datasource db {