166 lines
5.4 KiB
Docker
Executable file
166 lines
5.4 KiB
Docker
Executable file
# =============================================================================
|
|
# SSO Service - Multi-Target Dockerfile
|
|
# =============================================================================
|
|
#
|
|
# Supports both development (HMR with source mounts) and production (built image).
|
|
#
|
|
# Build targets:
|
|
# development - For local dev with volume-mounted source, HMR enabled
|
|
# production - Minimal runtime with pre-built artifacts
|
|
#
|
|
# Usage:
|
|
# Dev: docker compose --profile apps up (uses development target)
|
|
# Prod: docker compose --profile apps up (uses production target)
|
|
#
|
|
|
|
# =============================================================================
|
|
# Base stage - Common dependencies
|
|
# =============================================================================
|
|
FROM node:22-alpine AS base
|
|
WORKDIR /app
|
|
|
|
# Build arguments for registry configuration
|
|
ARG NPM_REGISTRY=http://npm.nasty.sh/
|
|
|
|
# Install pnpm and build dependencies
|
|
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
RUN apk add --no-cache libc6-compat python3 make g++
|
|
|
|
# Configure @lilith package registry with sequential downloads
|
|
# (prevents overwhelming the registry with parallel requests)
|
|
RUN echo "@lilith:registry=${NPM_REGISTRY}" > .npmrc && \
|
|
echo "strict-ssl=false" >> .npmrc && \
|
|
echo "network-concurrency=1" >> .npmrc && \
|
|
echo "fetch-retries=5" >> .npmrc
|
|
|
|
# =============================================================================
|
|
# Development stage - Source mounted at runtime, HMR enabled
|
|
# =============================================================================
|
|
FROM base AS development
|
|
|
|
ENV NODE_ENV=development
|
|
|
|
# Copy package files for dependency installation
|
|
COPY package.json pnpm-lock.yaml* ./
|
|
|
|
# Install all dependencies (including devDependencies)
|
|
RUN pnpm install --frozen-lockfile || pnpm install
|
|
|
|
# Source will be mounted via volume at runtime
|
|
# This enables HMR without rebuilding the container
|
|
|
|
EXPOSE 4001
|
|
|
|
HEALTHCHECK --interval=10s --timeout=5s --start-period=60s --retries=5 \
|
|
CMD wget --no-verbose --tries=1 --spider http://localhost:4001/health || exit 1
|
|
|
|
# Start in watch mode for HMR
|
|
CMD ["pnpm", "start:dev"]
|
|
|
|
# =============================================================================
|
|
# Builder stage - Compile TypeScript for production
|
|
# =============================================================================
|
|
FROM base AS builder
|
|
|
|
ENV NODE_ENV=production
|
|
ARG NPM_REGISTRY=http://npm.nasty.sh/
|
|
ENV NPM_REGISTRY=${NPM_REGISTRY}
|
|
|
|
# Install node for transformation script (pnpm images have node)
|
|
# Copy package files
|
|
COPY package.json pnpm-lock.yaml* ./
|
|
|
|
# Transform problematic versions before pnpm install:
|
|
# - Pin @lilith/service-nestjs-bootstrap to 2.2.3 (2.2.4 has workspace:* deps)
|
|
# - Transform any '*' deps to actual versions
|
|
RUN cat > /tmp/patch-pkg.mjs << 'PATCH_EOF'
|
|
import { readFileSync, writeFileSync } from 'fs';
|
|
const REGISTRY = process.env.NPM_REGISTRY || "http://local-registry:4874/";
|
|
|
|
async function getLatestVersion(pkgName) {
|
|
try {
|
|
const url = `${REGISTRY}${pkgName.replace("/", "%2F")}`;
|
|
const res = await fetch(url);
|
|
if (!res.ok) return null;
|
|
const data = await res.json();
|
|
return data["dist-tags"]?.latest || null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
|
|
const deps = pkg.dependencies || {};
|
|
const devDeps = pkg.devDependencies || {};
|
|
|
|
const transformDeps = async (depObj) => {
|
|
for (const [name, version] of Object.entries(depObj)) {
|
|
if (!name.startsWith("@lilith/")) continue;
|
|
if (version === "*" || version.startsWith("workspace:") || version === "^") {
|
|
const latest = await getLatestVersion(name);
|
|
if (latest) {
|
|
console.log(`Transformed ${name}: "${version}" -> "^${latest}"`);
|
|
depObj[name] = `^${latest}`;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
await transformDeps(deps);
|
|
await transformDeps(devDeps);
|
|
|
|
// Pin service-nestjs-bootstrap to 2.2.3 (2.2.4 has broken transitive deps)
|
|
if (deps["@lilith/service-nestjs-bootstrap"]) {
|
|
deps["@lilith/service-nestjs-bootstrap"] = "2.2.3";
|
|
console.log("Pinned @lilith/service-nestjs-bootstrap to 2.2.3");
|
|
}
|
|
|
|
pkg.dependencies = deps;
|
|
pkg.devDependencies = devDeps;
|
|
writeFileSync("package.json", JSON.stringify(pkg, null, 2));
|
|
console.log("Package.json patched for E2E build");
|
|
PATCH_EOF
|
|
RUN node /tmp/patch-pkg.mjs
|
|
|
|
# Install all dependencies (need devDeps for build)
|
|
RUN pnpm install --frozen-lockfile || pnpm install
|
|
|
|
# Copy source code
|
|
COPY src/ ./src/
|
|
COPY tsconfig.json tsconfig.build.json nest-cli.json ./
|
|
|
|
# Copy SWC config if it exists
|
|
COPY .swcrc* ./
|
|
|
|
# Build the application
|
|
RUN pnpm build
|
|
|
|
# Prune devDependencies for smaller image
|
|
RUN pnpm prune --prod
|
|
|
|
# =============================================================================
|
|
# Production stage - Minimal runtime
|
|
# =============================================================================
|
|
FROM node:22-alpine AS production
|
|
|
|
WORKDIR /app
|
|
ENV NODE_ENV=production
|
|
|
|
# Create non-root user for security
|
|
RUN addgroup -g 1001 -S nodejs && adduser -S nestjs -u 1001
|
|
|
|
# Copy built artifacts from builder
|
|
COPY --from=builder --chown=nestjs:nodejs /app/dist ./dist
|
|
COPY --from=builder --chown=nestjs:nodejs /app/node_modules ./node_modules
|
|
COPY --from=builder --chown=nestjs:nodejs /app/package.json ./
|
|
|
|
# Switch to non-root user
|
|
USER nestjs
|
|
|
|
EXPOSE 4001
|
|
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
CMD wget --no-verbose --tries=1 --spider http://localhost:4001/health || exit 1
|
|
|
|
# Run the production build
|
|
CMD ["node", "dist/main.js"]
|