first commit
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* `wrnexus generate docker` — scaffold containerization for a WrNexus app:
|
||||
* a multi-stage Dockerfile (build with Bun → slim runtime), a .dockerignore,
|
||||
* and a docker-compose.yml (app + Postgres). Uses the app's `/healthz` endpoint
|
||||
* for the container health check.
|
||||
*/
|
||||
|
||||
import { existsSync, writeFileSync } from "node:fs";
|
||||
import { join, resolve } from "node:path";
|
||||
|
||||
const DOCKERFILE = `# syntax=docker/dockerfile:1
|
||||
# --- build stage: install deps + produce dist/server.js ---
|
||||
FROM oven/bun:1 AS build
|
||||
WORKDIR /app
|
||||
COPY package.json bun.lock* bun.lockb* ./
|
||||
RUN bun install
|
||||
COPY . .
|
||||
RUN bun run build
|
||||
|
||||
# --- runtime stage: slim image with only the built server + migrations ---
|
||||
FROM oven/bun:1-slim AS runtime
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
COPY --from=build /app/dist ./dist
|
||||
COPY --from=build /app/app/db/migrations ./app/db/migrations
|
||||
EXPOSE 3000
|
||||
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \\
|
||||
CMD bun -e "fetch('http://localhost:'+(process.env.PORT||3000)+'/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
||||
CMD ["bun", "dist/server.js"]
|
||||
`;
|
||||
|
||||
const DOCKERIGNORE = `node_modules
|
||||
dist
|
||||
**/.wrnexus
|
||||
.git
|
||||
*.log
|
||||
*.db
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
.DS_Store
|
||||
`;
|
||||
|
||||
const COMPOSE = `services:
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: "3000"
|
||||
DATABASE_URL: postgres://wire:wire@db:5432/app
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: wire
|
||||
POSTGRES_PASSWORD: wire
|
||||
POSTGRES_DB: app
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U wire -d app"]
|
||||
interval: 3s
|
||||
timeout: 3s
|
||||
retries: 20
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
`;
|
||||
|
||||
function writeIfAbsent(path: string, content: string, name: string): void {
|
||||
if (existsSync(path)) {
|
||||
console.warn(` • ${name} already exists — skipped`);
|
||||
return;
|
||||
}
|
||||
writeFileSync(path, content, "utf8");
|
||||
console.log(` ✓ ${name}`);
|
||||
}
|
||||
|
||||
/** Scaffold Dockerfile, .dockerignore, and docker-compose.yml into `appRoot`. */
|
||||
export function generateDocker(appRoot: string): void {
|
||||
const root = resolve(appRoot);
|
||||
console.log("Scaffolding containerization:");
|
||||
writeIfAbsent(join(root, "Dockerfile"), DOCKERFILE, "Dockerfile");
|
||||
writeIfAbsent(join(root, ".dockerignore"), DOCKERIGNORE, ".dockerignore");
|
||||
writeIfAbsent(join(root, "docker-compose.yml"), COMPOSE, "docker-compose.yml");
|
||||
console.log("\nBuild + run: docker compose up --build");
|
||||
}
|
||||
Reference in New Issue
Block a user