From 515d1084dae4937ef97fa1223fd740e59921180f Mon Sep 17 00:00:00 2001 From: jubnl Date: Mon, 25 May 2026 20:43:03 +0200 Subject: [PATCH] fix(docker): update Dockerfile and CI for monorepo workspace structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dockerfile: - Add shared-builder stage that produces @trek/shared dist before client and server stages need it - Each build stage carries root package.json + package-lock.json so npm can resolve @trek/shared as a workspace dependency - Production stage installs via workspace context (npm ci --workspace=server --omit=dev) so node_modules/@trek/shared symlinks to shared/dist correctly - Copy server/tsconfig.json into the image so tsconfig-paths/register can find the MCP SDK path aliases at runtime - CMD cds into /app/server before starting node so tsconfig-paths baseUrl resolves and ../node_modules points to /app/node_modules - Remove mkdir for /app/server (now a real dir); keep symlinks for uploads/data docker.yml version-bump: - Replace manual per-workspace cd+npm-version calls with single: npm version --workspaces --include-workspace-root --no-git-tag-version (mirrors the version:* scripts in root package.json) - git add now references root package-lock.json; adds shared/package.json .dockerignore: add shared/dist package.json: fix version:prerelease preid (alpha → pre) --- .dockerignore | 1 + .github/workflows/docker.yml | 7 ++-- Dockerfile | 68 +++++++++++++++++++++++------------- package.json | 2 +- 4 files changed, 49 insertions(+), 29 deletions(-) diff --git a/.dockerignore b/.dockerignore index 65f3dcd0..f3e717c0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,6 +2,7 @@ node_modules client/node_modules server/node_modules client/dist +shared/dist data uploads .git diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ef038083..21e518bf 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -102,16 +102,15 @@ jobs: echo "VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT echo "$STABLE → $NEW_VERSION ($BUMP)" - # Update package.json files and Helm chart - cd server && npm version "$NEW_VERSION" --no-git-tag-version && cd .. - cd client && npm version "$NEW_VERSION" --no-git-tag-version && cd .. + # Update all workspace + root package.json files and the root lockfile in one shot + npm version "$NEW_VERSION" --workspaces --include-workspace-root --no-git-tag-version sed -i "s/^version: .*/version: $NEW_VERSION/" charts/trek/Chart.yaml sed -i "s/^appVersion: .*/appVersion: \"$NEW_VERSION\"/" charts/trek/Chart.yaml # Commit and tag git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git add server/package.json server/package-lock.json client/package.json client/package-lock.json charts/trek/Chart.yaml + git add package.json package-lock.json server/package.json client/package.json shared/package.json charts/trek/Chart.yaml git commit -m "chore: bump version to $NEW_VERSION [skip ci]" git tag "v$NEW_VERSION" git push origin main --follow-tags diff --git a/Dockerfile b/Dockerfile index bd9de728..ad29cf31 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,41 +1,60 @@ -# Stage 1: Build React client -FROM node:24-alpine AS client-builder -WORKDIR /app/client -COPY client/package*.json ./ -RUN npm ci -COPY client/ ./ -RUN npm run build +# ── Stage 1: shared ────────────────────────────────────────────────────────── +FROM node:24-alpine AS shared-builder +WORKDIR /app +COPY package.json package-lock.json ./ +COPY shared/package.json ./shared/ +RUN npm ci --workspace=shared +COPY shared/ ./shared/ +RUN npm run build --workspace=shared -# Stage 2: Build server (TypeScript -> dist via tsc + tsc-alias) -# --ignore-scripts: tsc only transpiles, so we skip native builds (better-sqlite3) -# here; the production stage builds the native module. +# ── Stage 2: client ────────────────────────────────────────────────────────── +FROM node:24-alpine AS client-builder +WORKDIR /app +COPY package.json package-lock.json ./ +COPY shared/package.json ./shared/ +COPY client/package.json ./client/ +RUN npm ci --workspace=client +COPY --from=shared-builder /app/shared/dist ./shared/dist +COPY client/ ./client/ +RUN npm run build --workspace=client + +# ── Stage 3: server ────────────────────────────────────────────────────────── +# --ignore-scripts skips native builds (better-sqlite3); they happen in the production stage. FROM node:24-alpine AS server-builder WORKDIR /app -COPY server/package*.json ./ -RUN npm ci --ignore-scripts -COPY server/ ./ -RUN npm run build +COPY package.json package-lock.json ./ +COPY shared/package.json ./shared/ +COPY server/package.json ./server/ +RUN npm ci --workspace=server --ignore-scripts +COPY --from=shared-builder /app/shared/dist ./shared/dist +COPY server/ ./server/ +RUN npm run build --workspace=server -# Stage 3: Production server (runs the compiled JS — NestJS DI needs the -# decorator metadata that tsc emits; the old tsx runtime did not). +# ── Stage 4: production runtime ────────────────────────────────────────────── FROM node:24-alpine - WORKDIR /app -# Timezone support + native deps (better-sqlite3 needs build tools) -COPY server/package*.json ./ +# Workspace manifests only — source never enters this stage. +COPY package.json package-lock.json ./ +COPY shared/package.json ./shared/ +COPY server/package.json ./server/ + +# better-sqlite3 native addon requires build tools; purged after install. RUN apk add --no-cache tzdata dumb-init su-exec python3 make g++ && \ - npm ci --production && \ - rm package-lock.json && \ + npm ci --workspace=server --omit=dev && \ apk del python3 make g++ && \ rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx -COPY --from=server-builder /app/dist ./dist +COPY --from=server-builder /app/server/dist ./server/dist +# tsconfig-paths/register reads this at runtime to resolve MCP SDK paths. +COPY server/tsconfig.json ./server/ +COPY --from=shared-builder /app/shared/dist ./shared/dist COPY --from=client-builder /app/client/dist ./public COPY --from=client-builder /app/client/public/fonts ./public/fonts RUN mkdir -p /app/data/logs /app/uploads/files /app/uploads/covers /app/uploads/avatars /app/uploads/photos && \ - mkdir -p /app/server && ln -s /app/uploads /app/server/uploads && ln -s /app/data /app/server/data && \ + ln -s /app/uploads /app/server/uploads && \ + ln -s /app/data /app/server/data && \ chown -R node:node /app ENV NODE_ENV=production @@ -49,4 +68,5 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ CMD wget -qO- http://localhost:3000/api/health || exit 1 ENTRYPOINT ["dumb-init", "--"] -CMD ["sh", "-c", "chown -R node:node /app/data /app/uploads 2>/dev/null || true; exec su-exec node node dist/index.js"] +# cd into server/ so tsconfig-paths/register finds tsconfig.json and ../node_modules resolves correctly. +CMD ["sh", "-c", "chown -R node:node /app/data /app/uploads 2>/dev/null || true; cd /app/server && exec su-exec node node --require tsconfig-paths/register dist/index.js"] diff --git a/package.json b/package.json index a259e7e9..1fcace6f 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "version:premajor": "npm version premajor --preid=rc --workspaces --include-workspace-root --no-git-tag-version", "version:preminor": "npm version preminor --preid=beta --workspaces --include-workspace-root --no-git-tag-version", "version:prepatch": "npm version prepatch --preid=alpha --workspaces --include-workspace-root --no-git-tag-version", - "version:prerelease": "npm version prerelease --preid=alpha --workspaces --include-workspace-root --no-git-tag-version", + "version:prerelease": "npm version prerelease --preid=pre --workspaces --include-workspace-root --no-git-tag-version", "dev": "npm run build --workspace=shared && concurrently --names shared,server,client \"npm run build:watch --workspace=shared\" \"npm run dev --workspace=server\" \"npm run dev --workspace=client\"", "build": "npm run build --workspace=shared && npm run build --workspace=server && npm run build --workspace=client", "test": "npm run test --workspace=shared && npm run test --workspace=server && npm run test --workspace=client",