Dockerfile 4.1 KB

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