Dockerfile 3.4 KB

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