@tekki-forge/generator-docker 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
- # Temporary Holding Version
1
+ # @tekki-forge/generator-docker
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Forge generator: IR -> Docker and Compose configuration.
4
+
5
+ Part of [Tekki Forge](https://www.npmjs.com/package/tekki-forge), which turns a `forge.config.ts` into a production-ready full-stack app. Apps created with `forge new` install this package for you; see the [tekki-forge](https://www.npmjs.com/package/tekki-forge) package for the documentation.
@@ -0,0 +1,7 @@
1
+ import { type AppIR, type GeneratedFile, type Generator } from "@tekki-forge/compiler";
2
+ /**
3
+ * Docker files are developer-owned after creation (scaffold): teams customize them.
4
+ * `forge docker --force` rewrites them from the current template.
5
+ */
6
+ export declare function dockerFiles(ir: AppIR, ownership?: GeneratedFile["ownership"]): GeneratedFile[];
7
+ export declare const dockerGenerator: Generator;
package/dist/index.js ADDED
@@ -0,0 +1,135 @@
1
+ import {} from "@tekki-forge/compiler";
2
+ import { stringify } from "yaml";
3
+ /**
4
+ * Docker files are developer-owned after creation (scaffold): teams customize them.
5
+ * `forge docker --force` rewrites them from the current template.
6
+ */
7
+ export function dockerFiles(ir, ownership = "scaffold") {
8
+ const db = ir.slug.replace(/-/g, "_");
9
+ const apiEnv = {
10
+ DATABASE_URL: `postgresql://app:app@postgres:5432/${db}`,
11
+ BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:-change-me-in-production-use-openssl-rand-base64-32}",
12
+ BETTER_AUTH_URL: "${PUBLIC_URL:-http://localhost:${WEB_PORT:-3000}}",
13
+ PORT: "4000",
14
+ SEED: "${SEED:-true}",
15
+ ADMIN_EMAIL: "${ADMIN_EMAIL:-admin@example.com}",
16
+ ADMIN_PASSWORD: "${ADMIN_PASSWORD:-admin12345}",
17
+ SMTP_URL: "${SMTP_URL:-}",
18
+ RESEND_API_KEY: "${RESEND_API_KEY:-}",
19
+ EMAIL_FROM: "${EMAIL_FROM:-}",
20
+ };
21
+ for (const p of ir.auth.social) {
22
+ const key = p.toUpperCase();
23
+ apiEnv[`${key}_CLIENT_ID`] = `\${${key}_CLIENT_ID:-}`;
24
+ apiEnv[`${key}_CLIENT_SECRET`] = `\${${key}_CLIENT_SECRET:-}`;
25
+ }
26
+ const dependsOn = { postgres: { condition: "service_healthy" } };
27
+ const services = {
28
+ postgres: {
29
+ image: "postgres:17-alpine",
30
+ restart: "unless-stopped",
31
+ environment: { POSTGRES_DB: db, POSTGRES_USER: "app", POSTGRES_PASSWORD: "app" },
32
+ ports: ["${POSTGRES_PORT:-5432}:5432"],
33
+ volumes: ["postgres_data:/var/lib/postgresql/data"],
34
+ healthcheck: { test: ["CMD-SHELL", `pg_isready -U app -d ${db}`], interval: "3s", timeout: "3s", retries: 20 },
35
+ },
36
+ };
37
+ const volumes = { postgres_data: null };
38
+ for (const plugin of ir.plugins) {
39
+ for (const [name, svc] of Object.entries(plugin.docker.services)) {
40
+ services[name] = { restart: "unless-stopped", ...svc };
41
+ dependsOn[name] = { condition: svc.healthcheck ? "service_healthy" : "service_started" };
42
+ }
43
+ for (const v of plugin.docker.volumes)
44
+ volumes[v] = null;
45
+ for (const e of plugin.env)
46
+ if (e.docker !== undefined)
47
+ apiEnv[e.name] = `\${${e.name}:-${e.docker}}`;
48
+ }
49
+ const workers = ir.plugins.some((p) => p.runtime.worker);
50
+ if (workers && ir.backups)
51
+ volumes.backups = null;
52
+ services.api = {
53
+ build: { context: ".", dockerfile: "Dockerfile.api" },
54
+ restart: "unless-stopped",
55
+ depends_on: dependsOn,
56
+ environment: apiEnv,
57
+ ports: ["${API_PORT:-4000}:4000"],
58
+ };
59
+ if (workers) {
60
+ services.worker = {
61
+ build: { context: ".", dockerfile: "Dockerfile.api" },
62
+ restart: "unless-stopped",
63
+ command: ["sh", "-c", "pnpm worker"],
64
+ depends_on: { ...dependsOn, api: { condition: "service_started" } },
65
+ environment: { ...apiEnv, SEED: "false", ...(ir.backups ? { FORGE_BACKUP_DIR: "/backups" } : {}) },
66
+ // Scheduled backups (backups in forge.config.ts) land here unless the storage plugin uses S3.
67
+ ...(ir.backups ? { volumes: ["backups:/backups"] } : {}),
68
+ };
69
+ }
70
+ services.web = {
71
+ build: { context: ".", dockerfile: "Dockerfile.web" },
72
+ restart: "unless-stopped",
73
+ depends_on: ["api"],
74
+ environment: { API_INTERNAL_URL: "http://api:4000" },
75
+ ports: ["${WEB_PORT:-3000}:3000"],
76
+ };
77
+ const compose = `# Generated by Forge. Safe to edit — \`forge generate\` never touches this file.\n${stringify({ name: ir.slug, services, volumes }, { lineWidth: 0, nullStr: "", aliasDuplicateObjects: false })}`;
78
+ const base = `FROM node:24-alpine AS base
79
+ ENV PNPM_HOME=/pnpm PATH=/pnpm:$PATH CI=true NEXT_TELEMETRY_DISABLED=1
80
+ RUN corepack enable
81
+ WORKDIR /app
82
+ `;
83
+ const dockerfileApi = `# Generated by Forge. Safe to edit.
84
+ ${base}
85
+ FROM base AS deps
86
+ COPY . .
87
+ RUN pnpm install --frozen-lockfile --filter "@app/api..." \\
88
+ && pnpm --filter @app/database exec prisma generate
89
+
90
+ FROM deps AS runner
91
+ ENV NODE_ENV=production
92
+ # pg_dump/psql matching the PostgreSQL 17 server, for scheduled backups (forge backup).
93
+ RUN apk add --no-cache postgresql17-client
94
+ WORKDIR /app/apps/api
95
+ EXPOSE 4000
96
+ CMD ["sh", "-c", "pnpm --filter @app/database exec prisma migrate deploy && if [ \\"$SEED\\" = \\"true\\" ]; then pnpm seed; fi && pnpm start"]
97
+ `;
98
+ const dockerfileWeb = `# Generated by Forge. Safe to edit.
99
+ ${base}
100
+ FROM base AS build
101
+ COPY . .
102
+ RUN pnpm install --frozen-lockfile --filter "@app/web..." \\
103
+ && pnpm --filter @app/web build
104
+
105
+ FROM base AS runner
106
+ ENV NODE_ENV=production HOSTNAME=0.0.0.0 PORT=3000
107
+ WORKDIR /app
108
+ COPY --from=build /app/apps/web/.next/standalone ./
109
+ COPY --from=build /app/apps/web/.next/static ./apps/web/.next/static
110
+ COPY --from=build /app/apps/web/public ./apps/web/public
111
+ EXPOSE 3000
112
+ CMD ["node", "apps/web/server.js"]
113
+ `;
114
+ const dockerignore = `**/node_modules
115
+ **/.next
116
+ **/dist
117
+ **/.turbo
118
+ **/src/generated/prisma
119
+ .env
120
+ .env.*
121
+ !.env.example
122
+ .git
123
+ `;
124
+ return [
125
+ { path: "docker-compose.yml", contents: compose, ownership },
126
+ { path: "Dockerfile.api", contents: dockerfileApi, ownership },
127
+ { path: "Dockerfile.web", contents: dockerfileWeb, ownership },
128
+ { path: ".dockerignore", contents: dockerignore, ownership },
129
+ ];
130
+ }
131
+ export const dockerGenerator = {
132
+ name: "docker",
133
+ generate: (ir) => dockerFiles(ir),
134
+ };
135
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "@tekki-forge/generator-docker",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Forge generator: IR -> Docker and Compose configuration",
5
+ "keywords": [
6
+ "code-generator",
7
+ "forge",
8
+ "full-stack",
9
+ "tekki-forge"
10
+ ],
11
+ "author": "Mahy KANE",
12
+ "license": "MIT",
13
+ "type": "module",
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "engines": {
26
+ "node": ">=22"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "dependencies": {
32
+ "yaml": "^2.9.1",
33
+ "@tekki-forge/compiler": "^0.1.0"
34
+ },
35
+ "scripts": {
36
+ "build": "tsc -p tsconfig.json",
37
+ "typecheck": "tsc -p tsconfig.json --noEmit"
38
+ }
6
39
  }