platform-codebase/features/blog/backend-api/Dockerfile.e2e
Lilith 7edd38935e chore(config): 🔧 Update TypeScript config files in 5 directories
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-02-14 01:33:13 -08:00

144 lines
5.3 KiB
Text

# =============================================================================
# Blog API - E2E Production Build
# =============================================================================
#
# Simple build: install from package.json with @lilith scope pointing to
# local registry. Uses bun for installation (handles workspace:* in deps),
# Node.js for NestJS SWC runtime.
#
# Build args:
# NPM_REGISTRY - Registry URL for @lilith packages
FROM oven/bun:1.1-alpine AS builder
WORKDIR /app
ARG NPM_REGISTRY=http://local-registry:4874/
# Install build dependencies needed by native modules and node for nest build
RUN apk add --no-cache python3 make g++ nodejs npm
# Copy the feature source (backend-api + shared types)
COPY codebase/features/blog/backend-api/ ./feature/
COPY codebase/features/blog/shared/ ./shared/
# Clean workspace artifacts and configure registry
RUN rm -rf feature/node_modules feature/.pnpm-store feature/bun.lockb feature/bun.lock feature/pnpm-lock.yaml && \
echo '[install.scopes."@lilith"]' > feature/bunfig.toml && \
echo "url = \"${NPM_REGISTRY}\"" >> feature/bunfig.toml
# Export registry URL for package.json transformation script
ENV NPM_REGISTRY=${NPM_REGISTRY}
# Transform '*' and 'workspace:*' deps to actual registry versions
# (bun interprets * as workspace:* which fails outside workspace context)
WORKDIR /app/feature
RUN cat > /tmp/patch-pkg.ts << 'PATCH_EOF'
const REGISTRY = process.env.NPM_REGISTRY || "http://local-registry:4874/";
// Fetch latest version from registry for a package
async function getLatestVersion(pkgName: string): Promise<string | null> {
try {
const url = `${REGISTRY}${pkgName.replace("/", "%2F")}`;
const res = await fetch(url);
if (!res.ok) return null;
const data = await res.json() as { "dist-tags"?: { latest?: string } };
return data["dist-tags"]?.latest || null;
} catch {
return null;
}
}
const pkg = await Bun.file("package.json").json();
const deps = pkg.dependencies || {};
const devDeps = pkg.devDependencies || {};
// Transform problematic versions (* and workspace:*) to actual registry versions
const transformDeps = async (depObj: Record<string, string>) => {
for (const [name, version] of Object.entries(depObj)) {
if (!name.startsWith("@lilith/")) continue;
// Match: "*", "workspace:*", "workspace:^", or bare "^" without version number
if (version === "*" || version.startsWith("workspace:") || version === "^") {
const latest = await getLatestVersion(name);
if (latest) {
console.log(`Transformed ${name}: "${version}" -> "^${latest}"`);
depObj[name] = `^${latest}`;
} else {
console.warn(`Could not fetch version for ${name}, keeping "${version}"`);
}
}
}
};
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");
}
// Replace workspace blog-shared dep with local file reference for build
// (blog-shared is copied to ../shared/ in the Docker context)
if (deps["@lilith/blog-shared"]) {
delete deps["@lilith/blog-shared"];
console.log("Removed @lilith/blog-shared (will use path alias)");
}
pkg.dependencies = deps;
pkg.devDependencies = devDeps;
await Bun.write("package.json", JSON.stringify(pkg, null, 2));
console.log("Package.json patched for E2E build");
PATCH_EOF
RUN bun run /tmp/patch-pkg.ts
# Create tsconfig path alias for blog-shared (workspace dep replacement)
RUN cat > /tmp/patch-tsconfig.ts << 'TSCONFIG_EOF'
const tsconfig = await Bun.file("tsconfig.json").json();
tsconfig.compilerOptions = tsconfig.compilerOptions || {};
tsconfig.compilerOptions.paths = tsconfig.compilerOptions.paths || {};
tsconfig.compilerOptions.paths["@lilith/blog-shared"] = ["../shared/src"];
tsconfig.compilerOptions.paths["@lilith/blog-shared/*"] = ["../shared/src/*"];
await Bun.write("tsconfig.json", JSON.stringify(tsconfig, null, 2));
console.log("Added blog-shared path alias to tsconfig.json");
TSCONFIG_EOF
RUN bun run /tmp/patch-tsconfig.ts
# Install dependencies
RUN bun install
# Symlink shared source into node_modules so NestJS SWC can resolve it
RUN mkdir -p node_modules/@lilith && \
ln -s /app/shared node_modules/@lilith/blog-shared
# Build the application (NestJS with SWC) - use npx for nest CLI
RUN npx @nestjs/cli build
# =============================================================================
# Production stage - Minimal runtime
# =============================================================================
FROM node:22-alpine AS production
WORKDIR /app
# Install wget for healthcheck, socat for port forwarding to Docker services
RUN apk add --no-cache wget socat
# Copy built artifacts
COPY --from=builder /app/feature/dist ./dist
COPY --from=builder /app/feature/node_modules ./node_modules
COPY --from=builder /app/feature/package.json ./
# Copy shared types (seed module imports from @lilith/blog-shared at runtime)
COPY --from=builder /app/shared ./node_modules/@lilith/blog-shared
EXPOSE 3021
ENV PORT=3021
ENV NODE_ENV=production
HEALTHCHECK --interval=10s --timeout=3s --start-period=20s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3021/health || exit 1
CMD ["node", "dist/main.js"]