From 7e05e83aa5b21df1b6942331455c5d7659f6136e Mon Sep 17 00:00:00 2001 From: Melvin Chia Date: Sun, 18 Jan 2026 21:18:54 +0800 Subject: [PATCH] build: enhance Dockerfiles and entrypoints for project builds --- docker/client/Dockerfile | 9 ++++++ docker/client/nginx.conf | 6 ++-- docker/db-init/Dockerfile | 15 ++++++++++ docker/db-init/entrypoint.sh | 2 +- docker/server/Dockerfile | 39 +++++++++++++++++++----- docker/server/entrypoint.sh | 58 +++++++++++------------------------- 6 files changed, 76 insertions(+), 53 deletions(-) diff --git a/docker/client/Dockerfile b/docker/client/Dockerfile index e99e284b1..b9ad9fe68 100644 --- a/docker/client/Dockerfile +++ b/docker/client/Dockerfile @@ -18,6 +18,15 @@ COPY . . RUN --mount=type=cache,target=/root/.bun/install/cache \ bun install --frozen-lockfile --linker isolated +# Build @lifeforge/log (required by forge CLI used in prebuild) +RUN cd /app/packages/lifeforge-log && bun run build + +# Build shared package (required by server types) +RUN cd /app/shared && bun run build + +# Build @lifeforge/server-utils (required by server types) +RUN cd /app/packages/lifeforge-server-utils && bun run build + # Build client RUN cd /app/client && bun run build diff --git a/docker/client/nginx.conf b/docker/client/nginx.conf index 6974c7de8..1554f73aa 100644 --- a/docker/client/nginx.conf +++ b/docker/client/nginx.conf @@ -8,7 +8,7 @@ server { gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript; - # Proxy API requests to server + # Proxy API requests to server (must come before static asset location) location /api/ { proxy_pass http://server:3636/; proxy_http_version 1.1; @@ -25,8 +25,8 @@ server { try_files $uri $uri/ /index.html; } - # Cache static assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + # Cache static assets in /assets/ directory only + location /assets/ { expires 1y; add_header Cache-Control "public, immutable"; } diff --git a/docker/db-init/Dockerfile b/docker/db-init/Dockerfile index 9bf2e4d8b..fc4f82592 100644 --- a/docker/db-init/Dockerfile +++ b/docker/db-init/Dockerfile @@ -19,6 +19,12 @@ COPY . . RUN --mount=type=cache,target=/root/.bun/install/cache \ bun install --frozen-lockfile --linker isolated +# Build @lifeforge/log (required by tools) +RUN cd /app/packages/lifeforge-log && bun run build + +# Build @lifeforge/server-utils (required by schema files) +RUN cd /app/packages/lifeforge-server-utils && bun run build + # Build forge CLI RUN cd /app/tools && bun run build @@ -46,6 +52,15 @@ COPY --from=builder /schemas/server/src/lib ./server/src/lib COPY --from=builder /app/shared/dist ./shared/dist COPY --from=builder /app/shared/package.json ./shared/package.json +# Copy @lifeforge/server-utils (required by schema files) +COPY --from=builder /app/packages/lifeforge-server-utils/dist ./packages/lifeforge-server-utils/dist +COPY --from=builder /app/packages/lifeforge-server-utils/package.json ./packages/lifeforge-server-utils/package.json + +# Create node_modules symlinks for package resolution +RUN mkdir -p node_modules/@lifeforge && \ + ln -sf /app/shared node_modules/shared && \ + ln -sf /app/packages/lifeforge-server-utils node_modules/@lifeforge/server-utils + # Install minimal dependencies for schema evaluation RUN echo '{"dependencies":{"zod":"^4.0.0"}}' > package.json && bun install diff --git a/docker/db-init/entrypoint.sh b/docker/db-init/entrypoint.sh index 25f605c98..e815b2129 100644 --- a/docker/db-init/entrypoint.sh +++ b/docker/db-init/entrypoint.sh @@ -8,7 +8,7 @@ echo "Generating database migrations..." mkdir -p /pb_data/pb_migrations # Generate and apply migrations using bundled forge CLI -cd /app && bun forge.js db push +cd /app && bun forge.js --log-level debug db push echo "Migrations applied successfully!" echo "=== DB Init Complete ===" diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile index ac914f109..a1c3e041e 100644 --- a/docker/server/Dockerfile +++ b/docker/server/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # ============================================ -# Builder stage - build server bundle +# Builder stage - build core server bundle only # ============================================ FROM oven/bun:alpine AS builder @@ -16,17 +16,28 @@ COPY . . RUN --mount=type=cache,target=/root/.bun/install/cache \ bun install --frozen-lockfile --linker isolated +# Build shared package +RUN cd /lifeforge/shared && bun run build + +# Build @lifeforge/log package +RUN cd /lifeforge/packages/lifeforge-log && bun run build + +# Build lifeforge-server-utils package +RUN cd /lifeforge/packages/lifeforge-server-utils && bun run build + # Build server bundle RUN cd /lifeforge/server && bun run build # Create cleaned package.json without workspace deps for production install RUN bun -e "const pkg = require('./server/package.json'); \ delete pkg.dependencies.shared; \ + delete pkg.dependencies['@lifeforge/log']; \ + delete pkg.dependencies['@lifeforge/server-utils']; \ delete pkg.devDependencies; \ require('fs').writeFileSync('./server/package.docker.json', JSON.stringify(pkg, null, 2))" # ============================================ -# Production stage - minimal runtime +# Production stage - core server only (no modules) # ============================================ FROM oven/bun:alpine AS production @@ -35,20 +46,28 @@ RUN apk add --no-cache curl WORKDIR /lifeforge -# Copy ONLY bundled server (no node_modules!) +# Copy bundled server COPY --from=builder /lifeforge/server/dist ./server/dist -# Copy server source for @functions imports (modules import from @functions/*) -COPY --from=builder /lifeforge/server/src ./server/src - -# Copy shared package for module imports +# Copy shared package COPY --from=builder /lifeforge/shared/dist ./shared/dist COPY --from=builder /lifeforge/shared/package.json ./shared/package.json -# Install server dependencies for module loading (using cleaned package.json without workspace deps) +# Copy @lifeforge/log package +COPY --from=builder /lifeforge/packages/lifeforge-log/dist ./packages/lifeforge-log/dist +COPY --from=builder /lifeforge/packages/lifeforge-log/package.json ./packages/lifeforge-log/package.json + +# Copy lifeforge-server-utils package (required by module bundles at runtime) +COPY --from=builder /lifeforge/packages/lifeforge-server-utils/dist ./packages/lifeforge-server-utils/dist +COPY --from=builder /lifeforge/packages/lifeforge-server-utils/package.json ./packages/lifeforge-server-utils/package.json + +# Install server dependencies COPY --from=builder /lifeforge/server/package.docker.json ./package.json RUN bun install --production +# Create apps directory for module mounting +RUN mkdir -p /lifeforge/apps + # Copy entrypoint COPY docker/server/entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh @@ -59,4 +78,8 @@ EXPOSE 3636 HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ CMD curl -f http://localhost:3636/ || exit 1 +# Modules should be mounted to /lifeforge/apps at runtime: +# docker run -v /path/to/modules:/lifeforge/apps lifeforge-server ENTRYPOINT ["/entrypoint.sh"] + + diff --git a/docker/server/entrypoint.sh b/docker/server/entrypoint.sh index 825ac14bc..61f4f8b61 100644 --- a/docker/server/entrypoint.sh +++ b/docker/server/entrypoint.sh @@ -8,49 +8,25 @@ until wget -q --spider http://db:8090/api/health 2>/dev/null; do done echo "PocketBase is ready!" -# Create symlinks for server path aliases so modules can resolve @functions/*, @lib/*, etc. -mkdir -p /lifeforge/node_modules -ln -sf /lifeforge/server/src/core/functions /lifeforge/node_modules/@functions -ln -sf /lifeforge/server/src/lib /lifeforge/node_modules/@lib -ln -sf /lifeforge/server/src/core /lifeforge/node_modules/@core -ln -sf /lifeforge/server/src/core/constants.ts /lifeforge/node_modules/@constants -ln -sf /lifeforge/server/src/core/schema /lifeforge/node_modules/@schema +# Create symlinks for externalized dependencies +mkdir -p /lifeforge/node_modules/@lifeforge + +# Symlink shared package ln -sf /lifeforge/shared /lifeforge/node_modules/shared -# Install module-specific dependencies (skip workspace deps that fail) -echo "Installing module dependencies..." -for dir in /lifeforge/apps/*/; do - if [ -f "${dir}package.json" ]; then - modname=$(basename "$dir") - # Only install if node_modules doesn't exist or is empty - if [ ! -d "${dir}node_modules" ] || [ -z "$(ls -A ${dir}node_modules 2>/dev/null)" ]; then - echo "Installing deps for $modname..." - # Create temp package.json without workspace deps, install, then restore - cd "$dir" - if [ -f package.json ]; then - # Remove workspace deps before install - bun -e " - const fs = require('fs'); - const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8')); - const original = JSON.stringify(pkg, null, 2); - fs.writeFileSync('package.json.bak', original); - if (pkg.dependencies) { - for (const [k,v] of Object.entries(pkg.dependencies)) { - if (v.startsWith('workspace:')) delete pkg.dependencies[k]; - } - } - fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2)); - " 2>/dev/null || true - bun install --production 2>/dev/null || true - # Restore original package.json - if [ -f package.json.bak ]; then - mv package.json.bak package.json - fi - fi - fi - fi -done -echo "Module dependencies installed." +# Symlink @lifeforge/log (required by server-utils) +ln -sf /lifeforge/packages/lifeforge-log /lifeforge/node_modules/@lifeforge/log + +# Symlink @lifeforge/server-utils (required by module bundles) +ln -sf /lifeforge/packages/lifeforge-server-utils /lifeforge/node_modules/@lifeforge/server-utils + +# Check if modules are mounted +if [ -d "/lifeforge/apps" ] && [ "$(ls -A /lifeforge/apps 2>/dev/null)" ]; then + module_count=$(ls -d /lifeforge/apps/*/ 2>/dev/null | wc -l | tr -d ' ') + echo "Found $module_count module(s) mounted at /lifeforge/apps" +else + echo "No modules mounted. Mount modules to /lifeforge/apps to enable them." +fi echo "Starting server..." cd /lifeforge/server