@@ -12,6 +12,7 @@ const ROOT: string = process.cwd();
1212const FUNCTIONS_DIR : string = path . resolve ( ROOT , 'functions' ) ;
1313const GENERATED_DIR : string = path . resolve ( ROOT , 'generated' ) ;
1414const TEMPLATES_DIR : string = path . resolve ( ROOT , 'templates' ) ;
15+ const SHARED_TEMPLATES_DIR : string = path . resolve ( TEMPLATES_DIR , 'shared' ) ;
1516
1617const 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
177178const 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
228215function 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
456348function 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.' ) ;
0 commit comments