Просмотр исходного кода

feat: modernize Dockerfile and entrypoint implementation

- Updated Dockerfile to use dhi.io/node:24-debian13-dev as base image.
- Added pruner stage for Docker-optimized monorepo subset.
- Implemented dependency installation and artifact production stages.
- Replaced shell entrypoint with TypeScript entrypoint for better control.
- Added heap size detection and privilege drop functionality in the entrypoint.
- Created tests for entrypoint functions to ensure reliability.
- Removed deprecated Dockerfile and associated files.
- Updated buildspec.yml to include DHI registry login.
Yuki Takei 1 месяц назад
Родитель
Сommit
69fe38373c

+ 5 - 5
.kiro/specs/official-docker-image/spec.json

@@ -1,9 +1,9 @@
 {
   "feature_name": "official-docker-image",
   "created_at": "2026-02-20T00:00:00.000Z",
-  "updated_at": "2026-02-20T15:30:00.000Z",
+  "updated_at": "2026-02-24T00:15:00.000Z",
   "language": "ja",
-  "phase": "design-generated",
+  "phase": "implemented",
   "approvals": {
     "requirements": {
       "generated": true,
@@ -11,12 +11,12 @@
     },
     "design": {
       "generated": true,
-      "approved": false
+      "approved": true
     },
     "tasks": {
       "generated": true,
-      "approved": false
+      "approved": true
     }
   },
-  "ready_for_implementation": false
+  "ready_for_implementation": true
 }

+ 24 - 0
.kiro/specs/official-docker-image/tasks.md

@@ -170,3 +170,27 @@ DHI runtime イメージ (`dhi.io/node:24-debian13`) には `/bin/sh` が存在
   - Phase 1 の 3 ステージ構成と同等の runtime 動作を維持していることを確認する
   - ビルドキャッシュの効率改善(ソースコード変更時に依存インストールがスキップされること)を検証する
   - _Requirements: 3.1, 3.2, 3.4_
+
+## Phase 3: 本番置換と CI/CD 対応
+
+> Phase 2 で 5 ステージ構成が安定した後に実施する。`apps/app/docker-new/` の成果物を `apps/app/docker/` に移動して旧ファイルを削除し、CI/CD パイプラインを DHI 対応に更新する。
+
+- [x] 8. 本番置換と CI/CD 対応
+- [x] 8.1 (P) docker-new ディレクトリから docker ディレクトリへの置換
+  - `apps/app/docker/` 内の旧ファイル(旧 `Dockerfile`、`docker-entrypoint.sh`、旧 `Dockerfile.dockerignore`)を削除する
+  - `apps/app/docker-new/` 内の全ファイル(`Dockerfile`、`docker-entrypoint.ts`、`docker-entrypoint.spec.ts`、`Dockerfile.dockerignore`)を `apps/app/docker/` に移動する
+  - `apps/app/docker-new/` ディレクトリを削除する
+  - `codebuild/` ディレクトリと `README.md` が `apps/app/docker/` 内に維持されていることを確認する
+  - Dockerfile 内の entrypoint コピーパス(`apps/app/docker-new/docker-entrypoint.ts`)を `apps/app/docker/docker-entrypoint.ts` に更新する
+  - _Requirements: 8.1, 8.2_
+
+- [x] 8.2 (P) buildspec.yml の DHI レジストリログイン追加
+  - `apps/app/docker/codebuild/buildspec.yml` の pre_build フェーズに `docker login dhi.io` コマンドを追加する
+  - DHI は Docker Hub 認証情報を使用するため、既存の `DOCKER_REGISTRY_PASSWORD` シークレットと `growimoogle` ユーザー名を再利用する
+  - buildspec.yml の Dockerfile パス(`./apps/app/docker/Dockerfile`)が置換後も正しいことを確認する
+  - _Requirements: 8.3, 8.4_
+
+- [x] 8.3 置換後の統合検証
+  - 置換後の `apps/app/docker/Dockerfile` で Docker ビルドが正常完了することを確認する
+  - 既存の外部参照(`codebuild.tf`、`.github/workflows/release.yml`、`ci-app.yml`、`update-readme.sh`)が正しく動作することを確認する
+  - _Requirements: 8.1, 8.2, 8.3, 8.4_

+ 0 - 137
apps/app/docker-new/Dockerfile

@@ -1,137 +0,0 @@
-# syntax=docker/dockerfile:1
-
-ARG OPT_DIR="/opt"
-ARG PNPM_HOME="/root/.local/share/pnpm"
-
-##
-## base — DHI dev image with pnpm + turbo
-##
-FROM dhi.io/node:24-debian13-dev AS base
-
-ARG OPT_DIR
-ARG PNPM_HOME
-
-WORKDIR $OPT_DIR
-
-# Install build dependencies
-RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
-    --mount=type=cache,target=/var/cache/apt,sharing=locked \
-  apt-get update && apt-get install -y --no-install-recommends ca-certificates wget
-
-# Install pnpm (standalone script, no version hardcoding)
-RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL=/bin/sh sh -
-ENV PNPM_HOME=$PNPM_HOME
-ENV PATH="$PNPM_HOME:$PATH"
-
-# Install turbo globally
-RUN --mount=type=cache,target=$PNPM_HOME/store,sharing=locked \
-  pnpm add turbo --global
-
-
-##
-## pruner — turbo prune for Docker-optimized monorepo subset
-##
-FROM base AS pruner
-
-ARG OPT_DIR
-
-WORKDIR $OPT_DIR
-
-COPY . .
-
-# Include @growi/pdf-converter because @growi/pdf-converter-client has a turbo
-# task dependency on @growi/pdf-converter#gen:swagger-spec (generates the OpenAPI
-# spec that orval uses to build the client). Without it, turbo cannot resolve
-# the cross-package task dependency in the pruned workspace.
-RUN turbo prune @growi/app @growi/pdf-converter --docker
-
-
-##
-## deps — dependency installation (layer cached when only source changes)
-##
-FROM base AS deps
-
-ARG OPT_DIR
-ARG PNPM_HOME
-
-ENV PNPM_HOME=$PNPM_HOME
-ENV PATH="$PNPM_HOME:$PATH"
-
-WORKDIR $OPT_DIR
-
-# Copy only package manifests and lockfile for dependency caching
-COPY --from=pruner $OPT_DIR/out/json/ .
-
-# Install build tools and dependencies
-RUN --mount=type=cache,target=$PNPM_HOME/store,sharing=locked \
-  pnpm add node-gyp --global
-RUN --mount=type=cache,target=$PNPM_HOME/store,sharing=locked \
-  pnpm install --frozen-lockfile
-
-
-##
-## builder — build + produce artifacts
-##
-FROM deps AS builder
-
-ARG OPT_DIR
-
-WORKDIR $OPT_DIR
-
-# Copy full source on top of installed dependencies
-COPY --from=pruner $OPT_DIR/out/full/ .
-
-# turbo prune does not include root-level config files in its output.
-# tsconfig.base.json is referenced by most packages via "extends": "../../tsconfig.base.json"
-COPY tsconfig.base.json .
-
-# Build
-RUN turbo run clean
-RUN turbo run build --filter @growi/app
-
-# Produce artifacts
-RUN pnpm deploy out --prod --filter @growi/app
-RUN rm -rf apps/app/node_modules && mv out/node_modules apps/app/node_modules
-RUN rm -rf apps/app/.next/cache
-
-# Stage artifacts into a clean directory for COPY --from
-RUN mkdir -p /tmp/release/apps/app && \
-  cp package.json /tmp/release/ && \
-  cp -a apps/app/.next apps/app/config apps/app/dist apps/app/public \
-       apps/app/resource apps/app/tmp apps/app/next.config.js \
-       apps/app/package.json apps/app/node_modules \
-       /tmp/release/apps/app/ && \
-  (cp apps/app/.env.production* /tmp/release/apps/app/ 2>/dev/null || true)
-
-
-##
-## release — DHI runtime (no shell, no additional binaries)
-##
-FROM dhi.io/node:24-debian13 AS release
-
-ARG OPT_DIR
-
-ENV NODE_ENV="production"
-ENV appDir="$OPT_DIR/growi"
-
-# Copy artifacts from builder (no shell required)
-WORKDIR ${appDir}
-COPY --from=builder --chown=node:node /tmp/release/ ${appDir}/
-
-# Copy TypeScript entrypoint
-COPY --chown=node:node apps/app/docker-new/docker-entrypoint.ts /docker-entrypoint.ts
-
-# Switch back to root for entrypoint (it handles privilege drop)
-USER root
-WORKDIR ${appDir}/apps/app
-
-# OCI standard labels
-LABEL org.opencontainers.image.source="https://github.com/weseek/growi"
-LABEL org.opencontainers.image.title="GROWI"
-LABEL org.opencontainers.image.description="Team collaboration wiki using Markdown"
-LABEL org.opencontainers.image.vendor="WESEEK, Inc."
-
-VOLUME /data
-EXPOSE 3000
-
-ENTRYPOINT ["node", "/docker-entrypoint.ts"]

+ 0 - 79
apps/app/docker-new/Dockerfile.dockerignore

@@ -1,79 +0,0 @@
-# ============================================================
-# Build artifacts and caches
-# ============================================================
-**/node_modules
-**/.next
-**/.turbo
-**/.pnpm-store
-**/coverage
-out
-
-# ============================================================
-# Version control
-# ============================================================
-.git
-
-# ============================================================
-# Docker files (prevent recursive inclusion)
-# ============================================================
-**/Dockerfile
-**/*.dockerignore
-
-# ============================================================
-# Unrelated apps
-# ============================================================
-apps/slackbot-proxy
-
-# ============================================================
-# Test files
-# ============================================================
-**/*.spec.*
-**/*.test.*
-**/test/
-**/__tests__/
-**/playwright/
-
-# ============================================================
-# Documentation (no .md files are needed for build)
-# ============================================================
-**/*.md
-
-# ============================================================
-# Local environment overrides
-# ============================================================
-.env.local
-.env.*.local
-
-# ============================================================
-# IDE and editor settings
-# ============================================================
-.vscode
-.idea
-**/.DS_Store
-
-# ============================================================
-# CI/CD, DevOps, and project management
-# ============================================================
-.changeset
-.devcontainer
-.github
-aws
-bin
-
-# ============================================================
-# Linter, formatter, and tool configs (not needed for build)
-# ============================================================
-**/.editorconfig
-**/.markdownlint.yml
-**/.prettier*
-**/.stylelintrc*
-**/biome.json
-**/lefthook.yml
-
-# ============================================================
-# AI agent configuration
-# ============================================================
-**/.claude
-**/.kiro
-**/.mcp.json
-**/.serena

+ 75 - 48
apps/app/docker/Dockerfile

@@ -1,110 +1,137 @@
-# syntax = docker/dockerfile:1.4
+# syntax=docker/dockerfile:1
 
 ARG OPT_DIR="/opt"
 ARG PNPM_HOME="/root/.local/share/pnpm"
 
 ##
-## base
+## base — DHI dev image with pnpm + turbo
 ##
-FROM node:20-slim AS base
+FROM dhi.io/node:24-debian13-dev AS base
 
 ARG OPT_DIR
 ARG PNPM_HOME
 
 WORKDIR $OPT_DIR
 
-# install tools
+# Install build dependencies
 RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
     --mount=type=cache,target=/var/cache/apt,sharing=locked \
-  apt-get update && apt-get install -y ca-certificates wget curl --no-install-recommends
+  apt-get update && apt-get install -y --no-install-recommends ca-certificates wget
 
-# install pnpm
-RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL="$(which sh)" PNPM_VERSION="10.4.1" sh -
+# Install pnpm (standalone script, no version hardcoding)
+RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL=/bin/sh sh -
 ENV PNPM_HOME=$PNPM_HOME
 ENV PATH="$PNPM_HOME:$PATH"
 
-# install turbo
+# Install turbo globally
 RUN --mount=type=cache,target=$PNPM_HOME/store,sharing=locked \
   pnpm add turbo --global
 
 
+##
+## pruner — turbo prune for Docker-optimized monorepo subset
+##
+FROM base AS pruner
+
+ARG OPT_DIR
+
+WORKDIR $OPT_DIR
+
+COPY . .
+
+# Include @growi/pdf-converter because @growi/pdf-converter-client has a turbo
+# task dependency on @growi/pdf-converter#gen:swagger-spec (generates the OpenAPI
+# spec that orval uses to build the client). Without it, turbo cannot resolve
+# the cross-package task dependency in the pruned workspace.
+RUN turbo prune @growi/app @growi/pdf-converter --docker
+
 
 ##
-## builder
+## deps — dependency installation (layer cached when only source changes)
 ##
-FROM base AS builder
+FROM base AS deps
+
+ARG OPT_DIR
+ARG PNPM_HOME
 
 ENV PNPM_HOME=$PNPM_HOME
 ENV PATH="$PNPM_HOME:$PATH"
 
 WORKDIR $OPT_DIR
 
-COPY . .
+# Copy only package manifests and lockfile for dependency caching
+COPY --from=pruner $OPT_DIR/out/json/ .
 
+# Install build tools and dependencies
 RUN --mount=type=cache,target=$PNPM_HOME/store,sharing=locked \
   pnpm add node-gyp --global
 RUN --mount=type=cache,target=$PNPM_HOME/store,sharing=locked \
-  pnpm install ---frozen-lockfile
+  pnpm install --frozen-lockfile
 
-# build
+
+##
+## builder — build + produce artifacts
+##
+FROM deps AS builder
+
+ARG OPT_DIR
+
+WORKDIR $OPT_DIR
+
+# Copy full source on top of installed dependencies
+COPY --from=pruner $OPT_DIR/out/full/ .
+
+# turbo prune does not include root-level config files in its output.
+# tsconfig.base.json is referenced by most packages via "extends": "../../tsconfig.base.json"
+COPY tsconfig.base.json .
+
+# Build
 RUN turbo run clean
 RUN turbo run build --filter @growi/app
 
-# make artifacts
+# Produce artifacts
 RUN pnpm deploy out --prod --filter @growi/app
 RUN rm -rf apps/app/node_modules && mv out/node_modules apps/app/node_modules
 RUN rm -rf apps/app/.next/cache
-RUN tar -zcf /tmp/packages.tar.gz \
-  package.json \
-  apps/app/.next \
-  apps/app/config \
-  apps/app/dist \
-  apps/app/public \
-  apps/app/resource \
-  apps/app/tmp \
-  apps/app/.env.production* \
-  apps/app/next.config.js \
-  apps/app/package.json \
-  apps/app/node_modules
 
+# Stage artifacts into a clean directory for COPY --from
+RUN mkdir -p /tmp/release/apps/app && \
+  cp package.json /tmp/release/ && \
+  cp -a apps/app/.next apps/app/config apps/app/dist apps/app/public \
+       apps/app/resource apps/app/tmp apps/app/next.config.js \
+       apps/app/package.json apps/app/node_modules \
+       /tmp/release/apps/app/ && \
+  (cp apps/app/.env.production* /tmp/release/apps/app/ 2>/dev/null || true)
 
 
 ##
-## release
+## release — DHI runtime (no shell, no additional binaries)
 ##
-FROM node:20-slim
-LABEL maintainer="Yuki Takei <yuki@weseek.co.jp>"
+FROM dhi.io/node:24-debian13 AS release
 
 ARG OPT_DIR
 
 ENV NODE_ENV="production"
-
 ENV appDir="$OPT_DIR/growi"
 
-# Add gosu
-# see: https://github.com/tianon/gosu/blob/1.13/INSTALL.md
-RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
-    --mount=type=cache,target=/var/cache/apt,sharing=locked \
-  set -eux; \
-	apt-get update; \
-	apt-get install -y gosu; \
-	rm -rf /var/lib/apt/lists/*; \
-# verify that the binary works
-	gosu nobody true
-
-# extract artifacts as 'node' user
-USER node
+# Copy artifacts from builder (no shell required)
 WORKDIR ${appDir}
-RUN --mount=type=bind,from=builder,source=/tmp/packages.tar.gz,target=/tmp/packages.tar.gz \
-  tar -zxf /tmp/packages.tar.gz -C ${appDir}/
+COPY --from=builder --chown=node:node /tmp/release/ ${appDir}/
 
-COPY --chown=node:node --chmod=700 apps/app/docker/docker-entrypoint.sh /
+# Copy TypeScript entrypoint
+COPY --chown=node:node apps/app/docker/docker-entrypoint.ts /docker-entrypoint.ts
 
+# Switch back to root for entrypoint (it handles privilege drop)
 USER root
 WORKDIR ${appDir}/apps/app
 
+# OCI standard labels
+LABEL org.opencontainers.image.source="https://github.com/weseek/growi"
+LABEL org.opencontainers.image.title="GROWI"
+LABEL org.opencontainers.image.description="Team collaboration wiki using Markdown"
+LABEL org.opencontainers.image.vendor="WESEEK, Inc."
+
 VOLUME /data
 EXPOSE 3000
 
-ENTRYPOINT ["/docker-entrypoint.sh"]
-CMD ["npm run migrate && node -r dotenv-flow/config --expose_gc dist/server/app.js"]
+ENTRYPOINT ["node", "/docker-entrypoint.ts"]

+ 74 - 4
apps/app/docker/Dockerfile.dockerignore

@@ -1,9 +1,79 @@
+# ============================================================
+# Build artifacts and caches
+# ============================================================
 **/node_modules
-**/coverage
-**/Dockerfile
-**/*.dockerignore
-**/.pnpm-store
 **/.next
 **/.turbo
+**/.pnpm-store
+**/coverage
 out
+
+# ============================================================
+# Version control
+# ============================================================
+.git
+
+# ============================================================
+# Docker files (prevent recursive inclusion)
+# ============================================================
+**/Dockerfile
+**/*.dockerignore
+
+# ============================================================
+# Unrelated apps
+# ============================================================
 apps/slackbot-proxy
+
+# ============================================================
+# Test files
+# ============================================================
+**/*.spec.*
+**/*.test.*
+**/test/
+**/__tests__/
+**/playwright/
+
+# ============================================================
+# Documentation (no .md files are needed for build)
+# ============================================================
+**/*.md
+
+# ============================================================
+# Local environment overrides
+# ============================================================
+.env.local
+.env.*.local
+
+# ============================================================
+# IDE and editor settings
+# ============================================================
+.vscode
+.idea
+**/.DS_Store
+
+# ============================================================
+# CI/CD, DevOps, and project management
+# ============================================================
+.changeset
+.devcontainer
+.github
+aws
+bin
+
+# ============================================================
+# Linter, formatter, and tool configs (not needed for build)
+# ============================================================
+**/.editorconfig
+**/.markdownlint.yml
+**/.prettier*
+**/.stylelintrc*
+**/biome.json
+**/lefthook.yml
+
+# ============================================================
+# AI agent configuration
+# ============================================================
+**/.claude
+**/.kiro
+**/.mcp.json
+**/.serena

+ 2 - 0
apps/app/docker/codebuild/buildspec.yml

@@ -12,6 +12,8 @@ phases:
     commands:
       # login to docker.io
       - echo ${DOCKER_REGISTRY_PASSWORD} | docker login --username growimoogle --password-stdin
+      # login to dhi.io (DHI uses Docker Hub credentials)
+      - echo ${DOCKER_REGISTRY_PASSWORD} | docker login dhi.io --username growimoogle --password-stdin
   build:
     commands:
       - docker build -t ${IMAGE_TAG} -f ./apps/app/docker/Dockerfile .

+ 0 - 18
apps/app/docker/docker-entrypoint.sh

@@ -1,18 +0,0 @@
-#!/bin/sh
-
-set -e
-
-# Support `FILE_UPLOAD=local`
-mkdir -p /data/uploads
-if [ ! -e "./public/uploads" ]; then
-  ln -s /data/uploads ./public/uploads
-fi
-chown -R node:node /data/uploads
-chown -h node:node ./public/uploads
-
-# Set permissions for shared directory for bulk export
-mkdir -p /tmp/page-bulk-export
-chown -R node:node /tmp/page-bulk-export
-chmod 700 /tmp/page-bulk-export
-
-exec gosu node /bin/bash -c "$@"

+ 0 - 0
apps/app/docker-new/docker-entrypoint.spec.ts → apps/app/docker/docker-entrypoint.spec.ts


+ 0 - 0
apps/app/docker-new/docker-entrypoint.ts → apps/app/docker/docker-entrypoint.ts