diff --git a/docker/api.Dockerfile b/docker/api.Dockerfile index 77f14ed..77ebdba 100644 --- a/docker/api.Dockerfile +++ b/docker/api.Dockerfile @@ -8,7 +8,9 @@ RUN corepack enable && corepack prepare pnpm@9.15.9 --activate FROM base AS deps # argon2's native addon has no musl prebuild -> compiles from source here. -RUN apk add --no-cache python3 make g++ +# openssl so `prisma generate` in the build stage sees the same platform the +# runtime stage does (see the binaryTargets note in schema.prisma). +RUN apk add --no-cache python3 make g++ openssl COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ COPY apps/api/package.json apps/api/package.json COPY apps/web/package.json apps/web/package.json @@ -32,7 +34,11 @@ ENV NODE_ENV=production # (mysqldump), restores (mysql), and the re-import pipeline (python + mdbtools) # from inside the API container. Build deps are installed in a throwaway virtual # package so pandas/pyarrow build on musl, then dropped from the final layer. -RUN apk add --no-cache python3 mdbtools mysql-client \ +# openssl is NOT optional: Prisma's query engine resolves its binary target at +# runtime (linux-musl-openssl-3.0.x) and aborts with "Please manually install +# OpenSSL" without it. Node bundles its own OpenSSL, so nothing else in this +# image pulls the system package in. +RUN apk add --no-cache python3 mdbtools mysql-client openssl \ && apk add --no-cache --virtual .pybuild python3-dev build-base \ && rm -rf /var/cache/apk/* @@ -40,6 +46,15 @@ COPY --from=build /repo/node_modules node_modules COPY --from=build /repo/packages/database packages/database COPY --from=build /repo/apps/api/dist apps/api/dist COPY --from=build /repo/apps/api/package.json apps/api/package.json +# node-linker=hoisted flattens EXTERNAL deps into /repo/node_modules, but the +# workspace dependency is still linked per-package: +# apps/api/node_modules/@jorgecuadros/database -> ../../../../packages/database +# Copying only /repo/node_modules therefore drops it and the API dies at boot +# with "Cannot find module '@jorgecuadros/database'". Copy just the scope dir — +# the rest of apps/api/node_modules is devDependencies (typescript) we don't +# want in the runtime layer. The relative link resolves because packages/database +# is copied to the same place above. +COPY --from=build /repo/apps/api/node_modules/@jorgecuadros apps/api/node_modules/@jorgecuadros # Migration scripts + their own Python venv (ops.service.ts prefers this venv). COPY migration migration diff --git a/packages/database/prisma/schema.prisma b/packages/database/prisma/schema.prisma index 1f51787..9d85813 100644 --- a/packages/database/prisma/schema.prisma +++ b/packages/database/prisma/schema.prisma @@ -8,6 +8,13 @@ generator client { provider = "prisma-client-js" output = "../generated/client" + // "native" covers local dev. The musl target is declared EXPLICITLY because + // Prisma picks the engine by sniffing the build environment: the Docker build + // stage has no openssl, so it detected plain "linux-musl", while the runtime + // stage (which needs openssl for other reasons) then demanded + // "linux-musl-openssl-3.0.x" and refused to start. Naming it here makes the + // engine that ships independent of what happens to be installed at build time. + binaryTargets = ["native", "linux-musl-openssl-3.0.x"] } datasource db {