Files
TehRiehlDeal 4c84d2fb96
CI / test (push) Successful in 31s
CI / lint (push) Successful in 27s
CI / secrets-scan (push) Successful in 5s
CI / vuln-scan (push) Successful in 13s
CI / sast (push) Successful in 9s
CI / build-images (push) Successful in 1m51s
CI / image-scan (push) Successful in 44s
CI / push (push) Successful in 32s
Bump Node 20 → 22 for native WebSocket support
The deployed backend was crashing at startup with `Node.js 20 detected
without native WebSocket support` from @supabase/realtime-js. Native
WebSocket landed in Node 22.4 — bumping the base image is cleaner than
shimming `ws` as a transport (no extra dep, no constructor wrapper).

Bumped in three places to keep everything aligned:
- tehriehlbudget-backend/Dockerfile (runtime + build stages)
- tehriehlbudget-frontend/Dockerfile (build stage; nginx runtime
  unaffected)
- .gitea/workflows/ci.yml (test + lint jobs use the same Node)

@types/node is already on ^22.10.7, so no type-side changes needed.

Bump backend and frontend to 0.1.6 (frontend forced by per-service
push gate; no functional change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 18:19:35 -07:00

57 lines
2.5 KiB
Docker

# syntax=docker/dockerfile:1.7
ARG NODE_VERSION=22
FROM node:${NODE_VERSION}-alpine AS builder
RUN apk add --no-cache libc6-compat openssl
RUN corepack enable && corepack prepare pnpm@9 --activate
WORKDIR /repo
# Hoisted layout so `prisma generate` writes to predictable paths
# (/repo/node_modules/{@prisma,.prisma}/) instead of into .pnpm/<hash>/.
# Local dev keeps the isolated layout — this .npmrc only exists in the
# build context.
RUN echo "node-linker=hoisted" > /repo/.npmrc
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
COPY tehriehlbudget-backend/ tehriehlbudget-backend/
COPY tehriehlbudget-frontend/package.json tehriehlbudget-frontend/
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile --filter tehriehlbudget-backend...
RUN pnpm --filter tehriehlbudget-backend exec prisma generate
RUN pnpm --filter tehriehlbudget-backend run build
# pnpm deploy produces a self-contained prod-only bundle for the backend:
# only the dependencies the runtime actually needs, no frontend tooling,
# no devDeps. Drops vite, vitest, react, the whole NestJS CLI etc. that
# were leaking into the runtime via the workspace's hoisted node_modules.
RUN pnpm --filter tehriehlbudget-backend --prod deploy /deploy
# pnpm deploy pulls a fresh @prisma/client from the pnpm store, which ships
# with an unpopulated `.prisma/client/index.js` stub (it throws "did not
# initialize yet" if loaded). Re-run `prisma generate` against the deploy
# tree so the schema-specific client (enums, models, etc.) lands in
# /deploy/node_modules/.prisma/client. The CLI is a devDep so it's not in
# /deploy — invoke it from /repo where the full install still has it.
RUN cd /deploy && /repo/node_modules/.bin/prisma generate \
--schema=./prisma/schema.prisma
FROM node:${NODE_VERSION}-alpine AS runtime
RUN apk add --no-cache libc6-compat openssl tini \
# Remove the npm CLI that ships with node:alpine. We use pnpm via
# corepack at build time and `node dist/main` at runtime — npm is
# never invoked. Its bundled deps (cross-spawn, glob, minimatch,
# tar) are the source of 11 of the 12 HIGH/CRITICAL CVEs Trivy flags
# in the image, and they're all in code paths we never execute.
&& rm -rf /usr/local/lib/node_modules/npm \
/usr/local/bin/npm \
/usr/local/bin/npx
WORKDIR /app
RUN addgroup -S nodeapp && adduser -S nodeapp -G nodeapp
ENV NODE_ENV=production
COPY --from=builder --chown=nodeapp:nodeapp /deploy/ ./
USER nodeapp
EXPOSE 3000
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "dist/main"]