263 lines
6.1 KiB
TypeScript
263 lines
6.1 KiB
TypeScript
/** Embedded templates are bundled with the CLI; no files are read at runtime. */
|
|
export const templateIds = [
|
|
"bun-service",
|
|
"bun-next",
|
|
"bun-compiled",
|
|
"node-pnpm",
|
|
"python-web",
|
|
"go-static",
|
|
"rust-static",
|
|
"static-nginx",
|
|
] as const;
|
|
|
|
export type DockerfileTemplateId = (typeof templateIds)[number];
|
|
|
|
export type DockerfileTemplate = {
|
|
id: DockerfileTemplateId;
|
|
label: string;
|
|
description: string;
|
|
};
|
|
|
|
export type RenderedDockerfileTemplate = {
|
|
dockerfile: string;
|
|
dockerignore: string;
|
|
};
|
|
|
|
const commonIgnore = `# Local state and credentials must not enter the build context.
|
|
.git
|
|
.github
|
|
.env
|
|
.env.*
|
|
**/.env
|
|
**/.env.*
|
|
*.pem
|
|
*.key
|
|
*.p12
|
|
id_rsa*
|
|
credentials.json
|
|
*.log
|
|
.DS_Store
|
|
`;
|
|
|
|
const templates: Record<
|
|
DockerfileTemplateId,
|
|
DockerfileTemplate & RenderedDockerfileTemplate
|
|
> = {
|
|
"bun-service": {
|
|
id: "bun-service",
|
|
label: "Bun service",
|
|
description:
|
|
"Bun app with package.json and bun.lock; starts src/index.ts on port 3000.",
|
|
dockerfile: `FROM oven/bun:1-alpine AS deps
|
|
WORKDIR /app
|
|
COPY package.json bun.lock ./
|
|
RUN bun install --frozen-lockfile --production
|
|
|
|
FROM oven/bun:1-alpine
|
|
WORKDIR /app
|
|
ENV NODE_ENV=production
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
COPY package.json ./
|
|
COPY src ./src
|
|
USER bun
|
|
EXPOSE 3000
|
|
CMD ["bun", "src/index.ts"]
|
|
`,
|
|
dockerignore: `${commonIgnore}node_modules
|
|
dist
|
|
.next
|
|
coverage
|
|
`,
|
|
},
|
|
"bun-next": {
|
|
id: "bun-next",
|
|
label: "Bun / Next.js standalone",
|
|
description:
|
|
"Next.js app with bun.lock and output: 'standalone' in next.config; starts generated server.js on port 3000.",
|
|
dockerfile: `FROM oven/bun:1-alpine AS build
|
|
WORKDIR /app
|
|
COPY package.json bun.lock ./
|
|
RUN bun install --frozen-lockfile
|
|
COPY . .
|
|
RUN mkdir -p public && bun run build
|
|
|
|
FROM oven/bun:1-alpine
|
|
WORKDIR /app
|
|
ENV NODE_ENV=production HOSTNAME=0.0.0.0 PORT=3000
|
|
COPY --from=build /app/.next/standalone ./
|
|
COPY --from=build /app/.next/static ./.next/static
|
|
COPY --from=build /app/public ./public
|
|
USER bun
|
|
EXPOSE 3000
|
|
CMD ["bun", "server.js"]
|
|
`,
|
|
dockerignore: `${commonIgnore}node_modules
|
|
.next
|
|
dist
|
|
coverage
|
|
`,
|
|
},
|
|
"bun-compiled": {
|
|
id: "bun-compiled",
|
|
label: "Compiled Bun executable",
|
|
description:
|
|
"Bun app with bun.lock and src/index.ts; compiles a Linux executable named app.",
|
|
dockerfile: `FROM oven/bun:1 AS build
|
|
WORKDIR /app
|
|
COPY package.json bun.lock ./
|
|
RUN bun install --frozen-lockfile
|
|
COPY . .
|
|
RUN bun build src/index.ts --compile --outfile /app/app
|
|
|
|
FROM oven/bun:1-slim
|
|
WORKDIR /app
|
|
COPY --from=build /app/app ./app
|
|
USER bun
|
|
EXPOSE 3000
|
|
CMD ["./app"]
|
|
`,
|
|
dockerignore: `${commonIgnore}node_modules
|
|
dist
|
|
.next
|
|
coverage
|
|
`,
|
|
},
|
|
"node-pnpm": {
|
|
id: "node-pnpm",
|
|
label: "Node / pnpm service",
|
|
description:
|
|
"Node app with pnpm-lock.yaml, a build script producing dist/index.js, and a packageManager pin in package.json.",
|
|
dockerfile: `FROM node:22-alpine AS build
|
|
WORKDIR /app
|
|
RUN corepack enable
|
|
COPY package.json pnpm-lock.yaml ./
|
|
RUN pnpm install --frozen-lockfile
|
|
COPY . .
|
|
RUN pnpm run build && pnpm prune --prod
|
|
|
|
FROM node:22-alpine
|
|
WORKDIR /app
|
|
ENV NODE_ENV=production
|
|
COPY --from=build /app/package.json ./
|
|
COPY --from=build /app/node_modules ./node_modules
|
|
COPY --from=build /app/dist ./dist
|
|
USER node
|
|
EXPOSE 3000
|
|
CMD ["node", "dist/index.js"]
|
|
`,
|
|
dockerignore: `${commonIgnore}node_modules
|
|
dist
|
|
coverage
|
|
.next
|
|
`,
|
|
},
|
|
"python-web": {
|
|
id: "python-web",
|
|
label: "Python web service",
|
|
description:
|
|
"Python app with requirements.txt and an ASGI app object at app:app; serves on port 8000 via uvicorn (include it in requirements.txt).",
|
|
dockerfile: `FROM python:3.12-slim AS build
|
|
WORKDIR /app
|
|
RUN python -m venv /opt/venv
|
|
ENV PATH=/opt/venv/bin:$PATH
|
|
COPY requirements.txt ./
|
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
|
|
FROM python:3.12-slim
|
|
WORKDIR /app
|
|
ENV PATH=/opt/venv/bin:$PATH PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
|
|
COPY --from=build /opt/venv /opt/venv
|
|
COPY . .
|
|
USER 10001:10001
|
|
EXPOSE 8000
|
|
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
`,
|
|
dockerignore: `${commonIgnore}__pycache__/
|
|
*.py[cod]
|
|
.venv/
|
|
venv/
|
|
.pytest_cache/
|
|
dist/
|
|
`,
|
|
},
|
|
"go-static": {
|
|
id: "go-static",
|
|
label: "Static Go binary",
|
|
description:
|
|
"Go module with go.mod and a main package at module root; builds a CGO-free app binary on port 8080.",
|
|
dockerfile: `FROM golang:1.24-alpine AS build
|
|
WORKDIR /src
|
|
COPY go.mod go.sum* ./
|
|
RUN go mod download
|
|
COPY . .
|
|
RUN CGO_ENABLED=0 go build -trimpath -o /app .
|
|
|
|
FROM scratch
|
|
COPY --from=build /app /app
|
|
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
|
USER 65532:65532
|
|
EXPOSE 8080
|
|
ENTRYPOINT ["/app"]
|
|
`,
|
|
dockerignore: `${commonIgnore}vendor/
|
|
bin/
|
|
coverage/
|
|
`,
|
|
},
|
|
"rust-static": {
|
|
id: "rust-static",
|
|
label: "Static Rust binary",
|
|
description:
|
|
"Cargo project with Cargo.lock and a binary named app (src/main.rs); no dynamic native-library dependencies; serves on port 8080.",
|
|
dockerfile: `FROM rust:1-alpine AS build
|
|
WORKDIR /app
|
|
RUN apk add --no-cache musl-dev
|
|
COPY Cargo.toml Cargo.lock ./
|
|
COPY src ./src
|
|
RUN cargo build --release --locked --bin app
|
|
|
|
FROM scratch
|
|
COPY --from=build /app/target/release/app /server
|
|
USER 65532:65532
|
|
EXPOSE 8080
|
|
ENTRYPOINT ["/server"]
|
|
`,
|
|
dockerignore: `${commonIgnore}target/
|
|
coverage/
|
|
`,
|
|
},
|
|
"static-nginx": {
|
|
id: "static-nginx",
|
|
label: "Static site / nginx",
|
|
description:
|
|
"Static site with ready-to-serve files in dist/; nginx serves them on port 80.",
|
|
dockerfile: `FROM nginx:1-alpine
|
|
COPY dist/ /usr/share/nginx/html/
|
|
EXPOSE 80
|
|
CMD ["nginx", "-g", "daemon off;"]
|
|
`,
|
|
dockerignore: `${commonIgnore}node_modules
|
|
.next
|
|
coverage
|
|
`,
|
|
},
|
|
};
|
|
|
|
export function listDockerfileTemplates(): DockerfileTemplate[] {
|
|
return templateIds.map((id) => {
|
|
const { label, description } = templates[id];
|
|
return { id, label, description };
|
|
});
|
|
}
|
|
|
|
export function renderDockerfileTemplate(
|
|
id: DockerfileTemplateId,
|
|
): RenderedDockerfileTemplate {
|
|
const template = templates[id];
|
|
if (!template) throw new Error(`Unknown Dockerfile template: ${id}`);
|
|
return {
|
|
dockerfile: template.dockerfile,
|
|
dockerignore: template.dockerignore,
|
|
};
|
|
}
|