Dockerfile 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. # syntax=docker/dockerfile:1
  2. ARG NODE_VERSION=24
  3. ARG OPT_DIR="/opt"
  4. ARG PNPM_HOME="/root/.local/share/pnpm"
  5. ##
  6. ## base — official Node.js image with pnpm + turbo
  7. ##
  8. FROM node:24-bookworm AS base
  9. ARG OPT_DIR
  10. ARG PNPM_HOME
  11. WORKDIR $OPT_DIR
  12. # Install pnpm (standalone script, no version hardcoding)
  13. RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL=/bin/sh sh -
  14. ENV PNPM_HOME=$PNPM_HOME
  15. ENV PATH="$PNPM_HOME:$PNPM_HOME/bin:$PATH"
  16. # Install turbo globally
  17. RUN pnpm add turbo --global
  18. ##
  19. ## pruner — turbo prune for Docker-optimized monorepo subset
  20. ##
  21. FROM base AS pruner
  22. ARG OPT_DIR
  23. WORKDIR $OPT_DIR
  24. COPY . .
  25. # Include @growi/pdf-converter because @growi/pdf-converter-client has a turbo
  26. # task dependency on @growi/pdf-converter#gen:swagger-spec (generates the OpenAPI
  27. # spec that orval uses to build the client). Without it, turbo cannot resolve
  28. # the cross-package task dependency in the pruned workspace.
  29. RUN turbo prune @growi/app @growi/pdf-converter --docker
  30. ##
  31. ## deps — dependency installation (layer cached when only source changes)
  32. ##
  33. FROM base AS deps
  34. ARG OPT_DIR
  35. ARG PNPM_HOME
  36. ENV PNPM_HOME=$PNPM_HOME
  37. ENV PATH="$PNPM_HOME:$PNPM_HOME/bin:$PATH"
  38. WORKDIR $OPT_DIR
  39. # Copy only package manifests and lockfile for dependency caching
  40. COPY --from=pruner $OPT_DIR/out/json/ .
  41. # Install build tools and dependencies
  42. # No cache mount for global installs: standalone pnpm stores @pnpm/exe via symlinks into
  43. # $PNPM_HOME/store; overlaying that path with a BuildKit cache breaks the pnpm binary.
  44. RUN pnpm add node-gyp --global
  45. # Separate store path keeps the BuildKit cache mount away from the pnpm bootstrap store.
  46. # --ignore-scripts: postinstall (prisma generate) needs full source, runs in builder stage.
  47. RUN --mount=type=cache,target=/root/.pnpm-store,sharing=locked \
  48. pnpm install --frozen-lockfile --ignore-scripts --store-dir /root/.pnpm-store
  49. ##
  50. ## builder — build + produce artifacts
  51. ##
  52. FROM deps AS builder
  53. ARG OPT_DIR
  54. WORKDIR $OPT_DIR
  55. # Copy full source on top of installed dependencies
  56. COPY --from=pruner $OPT_DIR/out/full/ .
  57. # turbo prune does not include root-level config files in its output.
  58. # tsconfig.base.json is referenced by most packages via "extends": "../../tsconfig.base.json"
  59. COPY tsconfig.base.json .
  60. # Build
  61. RUN turbo run clean
  62. RUN turbo run build --filter @growi/app
  63. # Produce artifacts
  64. RUN bash apps/app/bin/assemble-prod.sh
  65. # Stage artifacts into a clean directory for COPY --from
  66. RUN mkdir -p /tmp/release/apps/app && \
  67. cp package.json /tmp/release/ && \
  68. cp -a node_modules /tmp/release/ && \
  69. cp -a apps/app/.next apps/app/config apps/app/dist apps/app/public \
  70. apps/app/resource apps/app/tmp \
  71. apps/app/package.json apps/app/node_modules \
  72. apps/app/next.config.js \
  73. /tmp/release/apps/app/ && \
  74. (cp apps/app/.env.production* /tmp/release/apps/app/ 2>/dev/null || true)
  75. ##
  76. ## release — DHI runtime (no shell, no additional binaries)
  77. ##
  78. FROM dhi.io/node:24-debian13 AS release
  79. ARG OPT_DIR
  80. ENV NODE_ENV="production"
  81. ENV appDir="$OPT_DIR/growi"
  82. # Copy artifacts from builder (no shell required)
  83. WORKDIR ${appDir}
  84. COPY --from=builder --chown=node:node /tmp/release/ ${appDir}/
  85. # Copy TypeScript entrypoint
  86. COPY --chown=node:node apps/app/docker/docker-entrypoint.ts /docker-entrypoint.ts
  87. # Switch back to root for entrypoint (it handles privilege drop)
  88. USER root
  89. WORKDIR ${appDir}/apps/app
  90. # OCI standard labels
  91. LABEL org.opencontainers.image.source="https://github.com/weseek/growi"
  92. LABEL org.opencontainers.image.title="GROWI"
  93. LABEL org.opencontainers.image.description="Team collaboration wiki using Markdown"
  94. LABEL org.opencontainers.image.vendor="WESEEK, Inc."
  95. VOLUME /data
  96. EXPOSE 3000
  97. ENTRYPOINT ["node", "/docker-entrypoint.ts"]