Skip to content

Commit b67ad69

Browse files
committed
refactor generate.ts: remove infrastructure concerns, add functions-registry configmap
generate.ts was mixing function scaffolding with infrastructure config, regenerating the entire job-service.yaml and kustomize overlays just to set 3 env vars. This made the manifest uneditable and added ~200 lines of string-based YAML construction. Changes: - Remove generateJobServiceYaml() — job-service.yaml is now hand-written and reads function data from a generated functions-registry ConfigMap - Replace generateJobServicePatch() with generateFunctionsConfigMap() — writes a simple ConfigMap instead of kustomize overlay machinery - Move shared template files (tsconfig, README) to templates/shared/ - Remove hardcoded SIMPLE_EMAIL_DRY_RUN/SEND_EMAIL_LINK_DRY_RUN from local-deployment template, moved to constructive ConfigMap - Add port validation (duplicate ports, reserved port 8080) - Fix fragile indexOf lookup with proper index-based iteration - Update package.json engines to >=22.0.0 (required for --experimental-strip-types)
1 parent 6f56f32 commit b67ad69

10 files changed

Lines changed: 85 additions & 158 deletions

File tree

k8s/overlays/local-simple/config.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@ data:
2121
API_DEFAULT_DATABASE_ID: "constructive"
2222

2323
LOG_TIMESTAMP: "true"
24+
25+
SIMPLE_EMAIL_DRY_RUN: "true"
26+
SEND_EMAIL_LINK_DRY_RUN: "true"

k8s/overlays/local-simple/job-service.yaml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# AUTO-GENERATED by scripts/generate.ts — do not edit manually.
2-
# To add a function, create functions/<name>/handler.json and run `pnpm generate`.
31
apiVersion: apps/v1
42
kind: Deployment
53
metadata:
@@ -24,6 +22,8 @@ spec:
2422
envFrom:
2523
- configMapRef:
2624
name: constructive
25+
- configMapRef:
26+
name: functions-registry
2727
- secretRef:
2828
name: pg-credentials
2929
env:
@@ -37,18 +37,12 @@ spec:
3737
value: "http://knative-job-service.constructive-functions.svc.cluster.local:8080"
3838
- name: JOBS_SUPPORT_ANY
3939
value: "false"
40-
- name: JOBS_SUPPORTED
41-
value: "knative-job-example,send-email-link,simple-email"
4240
- name: JOBS_CALLBACK_HOST
4341
value: "knative-job-service.constructive-functions.svc.cluster.local"
4442
- name: JOBS_CALLBACK_BASE_URL
4543
value: "http://knative-job-service.constructive-functions.svc.cluster.local:8080/callback"
4644
- name: KNATIVE_SERVICE_URL
4745
value: "constructive-functions.svc.cluster.local"
48-
- name: INTERNAL_GATEWAY_URL
49-
value: "http://knative-job-example.constructive-functions.svc.cluster.local"
50-
- name: INTERNAL_GATEWAY_DEVELOPMENT_MAP
51-
value: '{"knative-job-example":"http://knative-job-example.constructive-functions.svc.cluster.local","send-email-link":"http://send-email-link.constructive-functions.svc.cluster.local","simple-email":"http://simple-email.constructive-functions.svc.cluster.local"}'
5246
- name: HOSTNAME
5347
valueFrom:
5448
fieldRef:

k8s/overlays/local-simple/kustomization.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ resources:
1212
- ./pg-secret.yaml
1313
- ./mailgun-secret.yaml
1414
- ./server-bucket-secret.yaml
15+
# Functions registry (generated by pnpm generate)
16+
- ../../../generated/functions-configmap.yaml
1517
# Constructive servers + db setup
1618
- ./constructive-server.yaml
1719
- ./constructive-server-admin.yaml

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"access": "restricted"
1313
},
1414
"engines": {
15-
"node": ">=18.17.0"
15+
"node": ">=22.0.0"
1616
},
1717
"packageManager": "pnpm@10.12.2",
1818
"scripts": {

scripts/generate.ts

Lines changed: 68 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const ROOT: string = process.cwd();
1212
const FUNCTIONS_DIR: string = path.resolve(ROOT, 'functions');
1313
const GENERATED_DIR: string = path.resolve(ROOT, 'generated');
1414
const TEMPLATES_DIR: string = path.resolve(ROOT, 'templates');
15+
const SHARED_TEMPLATES_DIR: string = path.resolve(TEMPLATES_DIR, 'shared');
1516

1617
const DEFAULT_TEMPLATE = 'node-graphql';
1718

@@ -172,7 +173,7 @@ function ensureSymlink(target: string, linkPath: string): boolean {
172173
return true;
173174
}
174175

175-
// --- Skaffold & kustomize overlay generation ---
176+
// --- Skaffold & ConfigMap generation ---
176177

177178
const K8S_NAMESPACE = 'constructive-functions';
178179

@@ -182,47 +183,33 @@ interface FunctionInfo {
182183
port: number;
183184
}
184185

185-
function generateJobServicePatch(fn: FunctionInfo): void {
186-
const overlayDir = path.join(GENERATED_DIR, fn.dir, 'k8s', 'skaffold-overlay');
187-
if (!fs.existsSync(overlayDir)) {
188-
fs.mkdirSync(overlayDir, { recursive: true });
189-
}
190-
186+
function generateFunctionsConfigMap(fns: FunctionInfo[], perFunction?: FunctionInfo): void {
187+
const targetFns = perFunction ? [perFunction] : fns;
188+
const jobsSupported = targetFns.map((fn) => fn.name).join(',');
191189
const gatewayMap: Record<string, string> = {};
192-
gatewayMap[fn.name] = `http://${fn.name}.${K8S_NAMESPACE}.svc.cluster.local`;
193-
194-
const kustomization = [
195-
'apiVersion: kustomize.config.k8s.io/v1beta1',
196-
'kind: Kustomization',
197-
'resources:',
198-
' - ../../../../k8s/overlays/local-simple',
199-
'patches:',
200-
' - path: job-service-patch.yaml',
201-
'',
202-
].join('\n');
190+
for (const fn of targetFns) {
191+
gatewayMap[fn.name] = `http://${fn.name}.${K8S_NAMESPACE}.svc.cluster.local`;
192+
}
203193

204-
const patch = [
205-
'apiVersion: apps/v1',
206-
'kind: Deployment',
194+
const yaml = [
195+
'# AUTO-GENERATED by scripts/generate.ts — do not edit manually.',
196+
'apiVersion: v1',
197+
'kind: ConfigMap',
207198
'metadata:',
208-
' name: knative-job-service',
209-
'spec:',
210-
' template:',
211-
' spec:',
212-
' containers:',
213-
' - name: knative-job-service',
214-
' env:',
215-
' - name: JOBS_SUPPORTED',
216-
` value: "${fn.name}"`,
217-
' - name: INTERNAL_GATEWAY_URL',
218-
` value: "http://${fn.name}.${K8S_NAMESPACE}.svc.cluster.local"`,
219-
' - name: INTERNAL_GATEWAY_DEVELOPMENT_MAP',
220-
` value: '${JSON.stringify(gatewayMap)}'`,
199+
' name: functions-registry',
200+
'data:',
201+
` JOBS_SUPPORTED: "${jobsSupported}"`,
202+
` INTERNAL_GATEWAY_DEVELOPMENT_MAP: '${JSON.stringify(gatewayMap)}'`,
221203
'',
222204
].join('\n');
223205

224-
writeIfChanged(path.join(overlayDir, 'kustomization.yaml'), kustomization);
225-
writeIfChanged(path.join(overlayDir, 'job-service-patch.yaml'), patch);
206+
if (perFunction) {
207+
const dir = path.join(GENERATED_DIR, perFunction.dir, 'k8s');
208+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
209+
writeIfChanged(path.join(dir, 'functions-configmap.yaml'), yaml);
210+
} else {
211+
writeIfChanged(path.join(GENERATED_DIR, 'functions-configmap.yaml'), yaml);
212+
}
226213
}
227214

228215
function generateSkaffoldYaml(fns: FunctionInfo[]): void {
@@ -268,9 +255,10 @@ function generateSkaffoldYaml(fns: FunctionInfo[]): void {
268255
' manifests:',
269256
' kustomize:',
270257
' paths:',
271-
` - generated/${fn.dir}/k8s/skaffold-overlay`,
258+
' - k8s/overlays/local-simple',
272259
' rawYaml:',
273260
` - generated/${fn.dir}/k8s/local-deployment.yaml`,
261+
` - generated/${fn.dir}/k8s/functions-configmap.yaml`,
274262
' deploy:',
275263
' kubectl:',
276264
` defaultNamespace: ${K8S_NAMESPACE}`,
@@ -308,6 +296,7 @@ function generateSkaffoldYaml(fns: FunctionInfo[]): void {
308296
' - k8s/overlays/local-simple',
309297
' rawYaml:',
310298
allRawYaml,
299+
' - generated/functions-configmap.yaml',
311300
' deploy:',
312301
' kubectl:',
313302
` defaultNamespace: ${K8S_NAMESPACE}`,
@@ -354,103 +343,6 @@ function generateSkaffoldYaml(fns: FunctionInfo[]): void {
354343
}
355344
}
356345

357-
function generateJobServiceYaml(fns: FunctionInfo[]): void {
358-
const jobsSupported = fns.map((fn) => fn.name).join(',');
359-
const gatewayMap: Record<string, string> = {};
360-
for (const fn of fns) {
361-
gatewayMap[fn.name] = `http://${fn.name}.${K8S_NAMESPACE}.svc.cluster.local`;
362-
}
363-
364-
const yaml = [
365-
'# AUTO-GENERATED by scripts/generate.ts — do not edit manually.',
366-
'# To add a function, create functions/<name>/handler.json and run `pnpm generate`.',
367-
'apiVersion: apps/v1',
368-
'kind: Deployment',
369-
'metadata:',
370-
' name: knative-job-service',
371-
' labels:',
372-
' app: knative-job-service',
373-
'spec:',
374-
' replicas: 1',
375-
' selector:',
376-
' matchLabels:',
377-
' app: knative-job-service',
378-
' template:',
379-
' metadata:',
380-
' labels:',
381-
' app: knative-job-service',
382-
' spec:',
383-
' containers:',
384-
' - name: knative-job-service',
385-
' image: constructive-functions:local',
386-
' command: ["node"]',
387-
' args: ["job/service/dist/run.js"]',
388-
' envFrom:',
389-
' - configMapRef:',
390-
' name: constructive',
391-
' - secretRef:',
392-
' name: pg-credentials',
393-
' env:',
394-
' - name: NODE_ENV',
395-
' value: "development"',
396-
' - name: JOBS_SCHEMA',
397-
' value: "app_jobs"',
398-
' - name: INTERNAL_JOBS_CALLBACK_PORT',
399-
' value: "8080"',
400-
' - name: INTERNAL_JOBS_CALLBACK_URL',
401-
` value: "http://knative-job-service.${K8S_NAMESPACE}.svc.cluster.local:8080"`,
402-
' - name: JOBS_SUPPORT_ANY',
403-
' value: "false"',
404-
' - name: JOBS_SUPPORTED',
405-
` value: "${jobsSupported}"`,
406-
' - name: JOBS_CALLBACK_HOST',
407-
` value: "knative-job-service.${K8S_NAMESPACE}.svc.cluster.local"`,
408-
' - name: JOBS_CALLBACK_BASE_URL',
409-
` value: "http://knative-job-service.${K8S_NAMESPACE}.svc.cluster.local:8080/callback"`,
410-
' - name: KNATIVE_SERVICE_URL',
411-
` value: "${K8S_NAMESPACE}.svc.cluster.local"`,
412-
' - name: INTERNAL_GATEWAY_URL',
413-
` value: "http://${fns.map((fn) => fn.name).sort()[0] || 'unknown'}.${K8S_NAMESPACE}.svc.cluster.local"`,
414-
' - name: INTERNAL_GATEWAY_DEVELOPMENT_MAP',
415-
` value: '${JSON.stringify(gatewayMap)}'`,
416-
' - name: HOSTNAME',
417-
' valueFrom:',
418-
' fieldRef:',
419-
' fieldPath: metadata.name',
420-
' ports:',
421-
' - containerPort: 8080',
422-
' name: jobs-http',
423-
' resources:',
424-
' requests:',
425-
' memory: "128Mi"',
426-
' cpu: "100m"',
427-
' limits:',
428-
' memory: "512Mi"',
429-
' cpu: "500m"',
430-
'---',
431-
'apiVersion: v1',
432-
'kind: Service',
433-
'metadata:',
434-
' name: knative-job-service',
435-
' labels:',
436-
' app: knative-job-service',
437-
'spec:',
438-
' type: ClusterIP',
439-
' selector:',
440-
' app: knative-job-service',
441-
' ports:',
442-
' - name: jobs-http',
443-
' port: 8080',
444-
' targetPort: jobs-http',
445-
'',
446-
].join('\n');
447-
448-
const jobServicePath = path.join(ROOT, 'k8s', 'overlays', 'local-simple', 'job-service.yaml');
449-
if (writeIfChanged(jobServicePath, yaml)) {
450-
console.log(' Updated k8s/overlays/local-simple/job-service.yaml');
451-
}
452-
}
453-
454346
// --- Main ---
455347

456348
function main(): void {
@@ -498,6 +390,26 @@ function main(): void {
498390
if (changed) console.log(` - ${relPath}`);
499391
}
500392

393+
// Process shared templates (files that don't vary by template type)
394+
if (fs.existsSync(SHARED_TEMPLATES_DIR)) {
395+
const sharedFiles = walkTemplateFiles(SHARED_TEMPLATES_DIR);
396+
for (const relPath of sharedFiles) {
397+
const templateFile = path.join(SHARED_TEMPLATES_DIR, relPath);
398+
const outputFile = path.join(genDir, relPath);
399+
400+
const outputDir = path.dirname(outputFile);
401+
if (!fs.existsSync(outputDir)) {
402+
fs.mkdirSync(outputDir, { recursive: true });
403+
}
404+
405+
const templateContent = fs.readFileSync(templateFile, 'utf-8');
406+
const baseName = path.basename(relPath);
407+
const processed = processTemplateFile(baseName, templateContent, manifest, fnDir);
408+
const changed = writeIfChanged(outputFile, processed);
409+
if (changed) console.log(` - ${relPath} (shared)`);
410+
}
411+
}
412+
501413
// Symlink handler.ts
502414
const handlerTarget = path.join(fnDir, 'handler.ts');
503415
if (fs.existsSync(handlerTarget)) {
@@ -542,11 +454,23 @@ function main(): void {
542454
}
543455
}
544456

457+
// Validate no duplicate ports
458+
const portToFunction = new Map<number, string>();
459+
for (const m of allManifests) {
460+
if (m.port === 8080) {
461+
throw new Error(`Function "${m.name}" uses port 8080 which is reserved for job-service.`);
462+
}
463+
if (portToFunction.has(m.port!)) {
464+
throw new Error(`Port ${m.port} conflict: "${m.name}" and "${portToFunction.get(m.port!)}".`);
465+
}
466+
portToFunction.set(m.port!, m.name);
467+
}
468+
545469
const manifestData = {
546-
functions: allManifests.map((m) => ({
547-
name: m.name,
548-
dir: functions[allManifests.indexOf(m)],
549-
port: m.port,
470+
functions: functions.map((dir, i) => ({
471+
name: allManifests[i].name,
472+
dir,
473+
port: allManifests[i].port,
550474
})),
551475
};
552476

@@ -556,18 +480,20 @@ function main(): void {
556480
console.log(' Updated generated/functions-manifest.json');
557481
}
558482

559-
// --- Generate skaffold, kustomize overlays, and job-service config ---
483+
// --- Generate skaffold and functions-registry configmaps ---
560484
// Only when generating all functions (not --only mode)
561485
if (!onlyName) {
562486
const fnInfos: FunctionInfo[] = manifestData.functions as FunctionInfo[];
563487

488+
// Generate per-function configmaps (for single-function Skaffold profiles)
564489
for (const fn of fnInfos) {
565-
generateJobServicePatch(fn);
490+
generateFunctionsConfigMap(fnInfos, fn);
566491
}
567-
console.log(' Generated per-function kustomize overlays');
492+
// Generate aggregate configmap (for local-simple profile)
493+
generateFunctionsConfigMap(fnInfos);
494+
console.log(' Generated functions-registry configmaps');
568495

569496
generateSkaffoldYaml(fnInfos);
570-
generateJobServiceYaml(fnInfos);
571497
}
572498

573499
console.log('Done.');

skaffold.yaml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ profiles:
2323
manifests:
2424
kustomize:
2525
paths:
26-
- generated/example/k8s/skaffold-overlay
26+
- k8s/overlays/local-simple
2727
rawYaml:
2828
- generated/example/k8s/local-deployment.yaml
29+
- generated/example/k8s/functions-configmap.yaml
2930
deploy:
3031
kubectl:
3132
defaultNamespace: constructive-functions
@@ -66,9 +67,10 @@ profiles:
6667
manifests:
6768
kustomize:
6869
paths:
69-
- generated/send-email-link/k8s/skaffold-overlay
70+
- k8s/overlays/local-simple
7071
rawYaml:
7172
- generated/send-email-link/k8s/local-deployment.yaml
73+
- generated/send-email-link/k8s/functions-configmap.yaml
7274
deploy:
7375
kubectl:
7476
defaultNamespace: constructive-functions
@@ -109,9 +111,10 @@ profiles:
109111
manifests:
110112
kustomize:
111113
paths:
112-
- generated/simple-email/k8s/skaffold-overlay
114+
- k8s/overlays/local-simple
113115
rawYaml:
114116
- generated/simple-email/k8s/local-deployment.yaml
117+
- generated/simple-email/k8s/functions-configmap.yaml
115118
deploy:
116119
kubectl:
117120
defaultNamespace: constructive-functions
@@ -159,6 +162,7 @@ profiles:
159162
- generated/example/k8s/local-deployment.yaml
160163
- generated/send-email-link/k8s/local-deployment.yaml
161164
- generated/simple-email/k8s/local-deployment.yaml
165+
- generated/functions-configmap.yaml
162166
deploy:
163167
kubectl:
164168
defaultNamespace: constructive-functions

0 commit comments

Comments
 (0)