go-duck-cli 1.2.2 → 1.2.25
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 +40 -0
- package/generators/ai_docs.js +2 -1
- package/generators/devops.js +591 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
<p align="center">
|
|
14
14
|
<a href="https://badge.fury.io/js/go-duck-cli"><img src="https://badge.fury.io/js/go-duck-cli.svg" alt="npm version"></a>
|
|
15
15
|
<a href="https://opensource.org/licenses/ISC"><img src="https://img.shields.io/badge/License-ISC-blue.svg" alt="License: ISC"></a>
|
|
16
|
+

|
|
16
17
|
</p>
|
|
17
18
|
|
|
18
19
|
---
|
|
@@ -106,6 +107,15 @@ Ensure your development environment meets the following requirements:
|
|
|
106
107
|
* **Docker:** v20+
|
|
107
108
|
* **Composability:** v2+
|
|
108
109
|
|
|
110
|
+
## Quick‑Start
|
|
111
|
+
|
|
112
|
+
Follow these four steps to get a generated microservice running locally and in Kubernetes:
|
|
113
|
+
|
|
114
|
+
1. **Create**: `go-duck create -o ./my-app -c config.yaml`
|
|
115
|
+
2. **Enter**: `cd my-app`
|
|
116
|
+
3. **Build & Push**: `./push.sh my-registry/my-app:1.0.0`
|
|
117
|
+
4. **Deploy**: `kubectl apply -f devops/k8s/`
|
|
118
|
+
|
|
109
119
|
## 🚀 Scaffold & Run
|
|
110
120
|
|
|
111
121
|
Follow these steps to create and run a new microservice with GO-DUCK:
|
|
@@ -116,6 +126,36 @@ go-duck create -o ./my-app -c config.yaml
|
|
|
116
126
|
|
|
117
127
|
# 2. Enter the application directory
|
|
118
128
|
cd my-app
|
|
129
|
+
|
|
130
|
+
# 3. Build, tag, and push Docker image (updates app.yaml)
|
|
131
|
+
./push.sh my-registry/my-app:1.0.0
|
|
132
|
+
|
|
133
|
+
# 4. Deploy to Kubernetes
|
|
134
|
+
kubectl apply -f devops/k8s/
|
|
135
|
+
|
|
136
|
+
### 🚢 Deploy to Kubernetes & Push Images
|
|
137
|
+
|
|
138
|
+
The generated project includes a **devops/k8s** directory with ready‑to‑apply Kubernetes manifests:
|
|
139
|
+
|
|
140
|
+
- `mongo.yaml` – StatefulSet & Service for MongoDB.
|
|
141
|
+
- `minio.yaml` – Deployment & Service for MinIO object storage.
|
|
142
|
+
- `app.yaml` – Deployment for the generated Go microservice, referencing a placeholder image name (`<app‑name>:latest`). Replace the image with the tag you push.
|
|
143
|
+
|
|
144
|
+
A helper script **push.sh** (located at the project root) automates the Docker build, tag, push, and then updates the `app.yaml` image field:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
./push.sh my-registry/my-app:1.0.0
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
After pushing, deploy everything with:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
kubectl apply -f devops/k8s/
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
> [!NOTE]
|
|
157
|
+
> The K8s manifests use placeholder values (e.g., resource limits, storage class). Adjust them to match your cluster requirements before applying.
|
|
158
|
+
|
|
119
159
|
```
|
|
120
160
|
|
|
121
161
|
### 🏗️ Compiling Protobuf & gRPC Contracts
|
package/generators/ai_docs.js
CHANGED
|
@@ -126,7 +126,8 @@ export const generateAIDocs = async (config, entities, outputDir, enums, openEnt
|
|
|
126
126
|
agentInstructions += `### High-Velocity Commands:\n`;
|
|
127
127
|
agentInstructions += `- **Build Project**: \`go build ./...\`\n`;
|
|
128
128
|
agentInstructions += `- **Protobuf Compilation**: \`./generate.sh\` (or \`.\\generate.bat\` on Windows)\n`;
|
|
129
|
-
agentInstructions += `- **Local Dependencies & Dev Boot**: \`docker-compose up -d && go run main.go\`\n
|
|
129
|
+
agentInstructions += `- **Local Dependencies & Dev Boot**: \`docker-compose up -d && go run main.go\`\n`;
|
|
130
|
+
agentInstructions += `- **Docker Build & Push**: \`./push.sh\` (Builds and pushes the Docker image to registry)\n\n`;
|
|
130
131
|
agentInstructions += `### GDL Evolution & Schema Deletions:\n`;
|
|
131
132
|
agentInstructions += `- **Snapshot Merging (Multi-File GDL)**: The generator implements stateful snapshot merging. Running \`import-gdl\` on a single GDL file will NOT wipe out other entities; previous snapshots are retrieved from the \`.go-duck/\` state folder and merged automatically.\n`;
|
|
132
133
|
agentInstructions += `- **Altering/Dropping Fields**: To add, drop, or edit fields within an entity, update the fields inline in the GDL entity block. Running \`import-gdl\` generates targeted Goose SQL column-level migrations.\n`;
|
package/generators/devops.js
CHANGED
|
@@ -400,6 +400,502 @@ jobs:
|
|
|
400
400
|
]
|
|
401
401
|
}, null, 4);
|
|
402
402
|
|
|
403
|
+
const pushScript = `#!/usr/bin/env bash
|
|
404
|
+
# Exit immediately if a command exits with a non-zero status
|
|
405
|
+
set -e
|
|
406
|
+
|
|
407
|
+
# --- CONFIGURATION ---
|
|
408
|
+
# Edit these variables to hardcode your values, or leave them to fallback to environment variables.
|
|
409
|
+
DOCKER_USER="\${DOCKER_USER:-your-dockerhub-username}"
|
|
410
|
+
IMAGE_NAME="\${IMAGE_NAME:-${appName}}"
|
|
411
|
+
VERSION="\${VERSION:-1.0.0}"
|
|
412
|
+
# ---------------------
|
|
413
|
+
|
|
414
|
+
# Build the full image tag
|
|
415
|
+
TAG="\${DOCKER_USER}/\${IMAGE_NAME}:\${VERSION}"
|
|
416
|
+
|
|
417
|
+
echo "========================================="
|
|
418
|
+
echo "Building Docker image: \$TAG"
|
|
419
|
+
echo "========================================="
|
|
420
|
+
docker build -f devops/Dockerfile -t "\$TAG" .
|
|
421
|
+
|
|
422
|
+
echo "========================================="
|
|
423
|
+
echo "Pushing Docker image: \$TAG"
|
|
424
|
+
echo "========================================="
|
|
425
|
+
docker push "\$TAG"
|
|
426
|
+
|
|
427
|
+
echo "========================================="
|
|
428
|
+
echo "Successfully built and published \$TAG"
|
|
429
|
+
echo "========================================="
|
|
430
|
+
`;
|
|
431
|
+
|
|
432
|
+
const k8sAppYaml = `apiVersion: apps/v1
|
|
433
|
+
kind: Deployment
|
|
434
|
+
metadata:
|
|
435
|
+
name: ${appName}
|
|
436
|
+
labels:
|
|
437
|
+
app: ${appName}
|
|
438
|
+
spec:
|
|
439
|
+
replicas: 1
|
|
440
|
+
selector:
|
|
441
|
+
matchLabels:
|
|
442
|
+
app: ${appName}
|
|
443
|
+
template:
|
|
444
|
+
metadata:
|
|
445
|
+
labels:
|
|
446
|
+
app: ${appName}
|
|
447
|
+
spec:
|
|
448
|
+
containers:
|
|
449
|
+
- name: ${appName}
|
|
450
|
+
image: ${appName}:latest
|
|
451
|
+
env:
|
|
452
|
+
- name: GO_PROFILE
|
|
453
|
+
value: prod
|
|
454
|
+
- name: GO_DUCK_SERVER_REST_PORT
|
|
455
|
+
value: "${appPort}"
|
|
456
|
+
- name: GO_DUCK_DATASOURCE_HOST
|
|
457
|
+
value: postgres
|
|
458
|
+
- name: GO_DUCK_DATASOURCE_USERNAME
|
|
459
|
+
value: "${config.datasource?.username || 'postgres'}"
|
|
460
|
+
- name: GO_DUCK_DATASOURCE_PASSWORD
|
|
461
|
+
value: "${config.datasource?.password || 'password'}"
|
|
462
|
+
- name: GO_DUCK_DATASOURCE_DATABASE
|
|
463
|
+
value: "${config.datasource?.database || 'go_duck_master'}"
|
|
464
|
+
- name: GO_DUCK_DATASOURCE_PORT
|
|
465
|
+
value: "5432"
|
|
466
|
+
- name: GO_DUCK_CACHE_REDIS_HOST
|
|
467
|
+
value: redis:6379
|
|
468
|
+
- name: GO_DUCK_MESSAGING_MQTT_BROKER
|
|
469
|
+
value: tcp://mosquitto:1883
|
|
470
|
+
- name: GO_DUCK_MESSAGING_NATS_URL
|
|
471
|
+
value: nats://nats:4222
|
|
472
|
+
- name: GO_DUCK_TELEMETRY_OTEL_ENDPOINT
|
|
473
|
+
value: otel-collector:4317
|
|
474
|
+
- name: GO_DUCK_ELASTICSEARCH_ADDRESSES
|
|
475
|
+
value: http://elasticsearch:9200
|
|
476
|
+
- name: GO_DUCK_SECURITY_KEYCLOAK_HOST
|
|
477
|
+
value: http://keycloak:8080
|
|
478
|
+
ports:
|
|
479
|
+
- name: http
|
|
480
|
+
containerPort: ${appPort}
|
|
481
|
+
- name: grpc
|
|
482
|
+
containerPort: 9000
|
|
483
|
+
---
|
|
484
|
+
apiVersion: v1
|
|
485
|
+
kind: Service
|
|
486
|
+
metadata:
|
|
487
|
+
name: ${appName}
|
|
488
|
+
spec:
|
|
489
|
+
ports:
|
|
490
|
+
- name: http
|
|
491
|
+
port: ${appPort}
|
|
492
|
+
targetPort: http
|
|
493
|
+
- name: grpc
|
|
494
|
+
port: 9000
|
|
495
|
+
targetPort: grpc
|
|
496
|
+
selector:
|
|
497
|
+
app: ${appName}
|
|
498
|
+
`;
|
|
499
|
+
|
|
500
|
+
const k8sPostgresYaml = `apiVersion: v1
|
|
501
|
+
kind: PersistentVolumeClaim
|
|
502
|
+
metadata:
|
|
503
|
+
name: postgres-pvc
|
|
504
|
+
spec:
|
|
505
|
+
accessModes:
|
|
506
|
+
- ReadWriteOnce
|
|
507
|
+
resources:
|
|
508
|
+
requests:
|
|
509
|
+
storage: 1Gi
|
|
510
|
+
---
|
|
511
|
+
apiVersion: apps/v1
|
|
512
|
+
kind: Deployment
|
|
513
|
+
metadata:
|
|
514
|
+
name: postgres
|
|
515
|
+
labels:
|
|
516
|
+
app: postgres
|
|
517
|
+
spec:
|
|
518
|
+
replicas: 1
|
|
519
|
+
selector:
|
|
520
|
+
matchLabels:
|
|
521
|
+
app: postgres
|
|
522
|
+
template:
|
|
523
|
+
metadata:
|
|
524
|
+
labels:
|
|
525
|
+
app: postgres
|
|
526
|
+
spec:
|
|
527
|
+
containers:
|
|
528
|
+
- name: postgres
|
|
529
|
+
image: postgres:15.6-alpine
|
|
530
|
+
env:
|
|
531
|
+
- name: POSTGRES_USER
|
|
532
|
+
value: "${config.datasource?.username || 'postgres'}"
|
|
533
|
+
- name: POSTGRES_PASSWORD
|
|
534
|
+
value: "${config.datasource?.password || 'password'}"
|
|
535
|
+
- name: POSTGRES_DB
|
|
536
|
+
value: "${config.datasource?.database || 'go_duck_master'}"
|
|
537
|
+
ports:
|
|
538
|
+
- containerPort: 5432
|
|
539
|
+
volumeMounts:
|
|
540
|
+
- name: postgres-storage
|
|
541
|
+
mountPath: /var/lib/postgresql/data
|
|
542
|
+
volumes:
|
|
543
|
+
- name: postgres-storage
|
|
544
|
+
persistentVolumeClaim:
|
|
545
|
+
claimName: postgres-pvc
|
|
546
|
+
---
|
|
547
|
+
apiVersion: v1
|
|
548
|
+
kind: Service
|
|
549
|
+
metadata:
|
|
550
|
+
name: postgres
|
|
551
|
+
spec:
|
|
552
|
+
ports:
|
|
553
|
+
- port: 5432
|
|
554
|
+
selector:
|
|
555
|
+
app: postgres
|
|
556
|
+
`;
|
|
557
|
+
|
|
558
|
+
const k8sRedisYaml = `apiVersion: apps/v1
|
|
559
|
+
kind: Deployment
|
|
560
|
+
metadata:
|
|
561
|
+
name: redis
|
|
562
|
+
labels:
|
|
563
|
+
app: redis
|
|
564
|
+
spec:
|
|
565
|
+
replicas: 1
|
|
566
|
+
selector:
|
|
567
|
+
matchLabels:
|
|
568
|
+
app: redis
|
|
569
|
+
template:
|
|
570
|
+
metadata:
|
|
571
|
+
labels:
|
|
572
|
+
app: redis
|
|
573
|
+
spec:
|
|
574
|
+
containers:
|
|
575
|
+
- name: redis
|
|
576
|
+
image: redis:7.2.4-alpine
|
|
577
|
+
ports:
|
|
578
|
+
- containerPort: 6379
|
|
579
|
+
---
|
|
580
|
+
apiVersion: v1
|
|
581
|
+
kind: Service
|
|
582
|
+
metadata:
|
|
583
|
+
name: redis
|
|
584
|
+
spec:
|
|
585
|
+
ports:
|
|
586
|
+
- port: 6379
|
|
587
|
+
selector:
|
|
588
|
+
app: redis
|
|
589
|
+
`;
|
|
590
|
+
|
|
591
|
+
const k8sNatsYaml = `apiVersion: apps/v1
|
|
592
|
+
kind: Deployment
|
|
593
|
+
metadata:
|
|
594
|
+
name: nats
|
|
595
|
+
labels:
|
|
596
|
+
app: nats
|
|
597
|
+
spec:
|
|
598
|
+
replicas: 1
|
|
599
|
+
selector:
|
|
600
|
+
matchLabels:
|
|
601
|
+
app: nats
|
|
602
|
+
template:
|
|
603
|
+
metadata:
|
|
604
|
+
labels:
|
|
605
|
+
app: nats
|
|
606
|
+
spec:
|
|
607
|
+
containers:
|
|
608
|
+
- name: nats
|
|
609
|
+
image: nats:2.10.12-alpine
|
|
610
|
+
ports:
|
|
611
|
+
- containerPort: 4222
|
|
612
|
+
---
|
|
613
|
+
apiVersion: v1
|
|
614
|
+
kind: Service
|
|
615
|
+
metadata:
|
|
616
|
+
name: nats
|
|
617
|
+
spec:
|
|
618
|
+
ports:
|
|
619
|
+
- port: 4222
|
|
620
|
+
selector:
|
|
621
|
+
app: nats
|
|
622
|
+
`;
|
|
623
|
+
|
|
624
|
+
const k8sMosquittoYaml = `apiVersion: v1
|
|
625
|
+
kind: ConfigMap
|
|
626
|
+
metadata:
|
|
627
|
+
name: mosquitto-config
|
|
628
|
+
data:
|
|
629
|
+
mosquitto.conf: |
|
|
630
|
+
listener 1883
|
|
631
|
+
listener 9001
|
|
632
|
+
protocol websockets
|
|
633
|
+
allow_anonymous true
|
|
634
|
+
---
|
|
635
|
+
apiVersion: apps/v1
|
|
636
|
+
kind: Deployment
|
|
637
|
+
metadata:
|
|
638
|
+
name: mosquitto
|
|
639
|
+
labels:
|
|
640
|
+
app: mosquitto
|
|
641
|
+
spec:
|
|
642
|
+
replicas: 1
|
|
643
|
+
selector:
|
|
644
|
+
matchLabels:
|
|
645
|
+
app: mosquitto
|
|
646
|
+
template:
|
|
647
|
+
metadata:
|
|
648
|
+
labels:
|
|
649
|
+
app: mosquitto
|
|
650
|
+
spec:
|
|
651
|
+
containers:
|
|
652
|
+
- name: mosquitto
|
|
653
|
+
image: eclipse-mosquitto:2.0.18
|
|
654
|
+
ports:
|
|
655
|
+
- containerPort: 1883
|
|
656
|
+
- containerPort: 9001
|
|
657
|
+
volumeMounts:
|
|
658
|
+
- name: config-volume
|
|
659
|
+
mountPath: /mosquitto/config/mosquitto.conf
|
|
660
|
+
subPath: mosquitto.conf
|
|
661
|
+
volumes:
|
|
662
|
+
- name: config-volume
|
|
663
|
+
configMap:
|
|
664
|
+
name: mosquitto-config
|
|
665
|
+
---
|
|
666
|
+
apiVersion: v1
|
|
667
|
+
kind: Service
|
|
668
|
+
metadata:
|
|
669
|
+
name: mosquitto
|
|
670
|
+
spec:
|
|
671
|
+
ports:
|
|
672
|
+
- name: mqtt
|
|
673
|
+
port: 1883
|
|
674
|
+
- name: ws
|
|
675
|
+
port: 9001
|
|
676
|
+
selector:
|
|
677
|
+
app: mosquitto
|
|
678
|
+
`;
|
|
679
|
+
|
|
680
|
+
const k8sElasticsearchYaml = `apiVersion: apps/v1
|
|
681
|
+
kind: Deployment
|
|
682
|
+
metadata:
|
|
683
|
+
name: elasticsearch
|
|
684
|
+
labels:
|
|
685
|
+
app: elasticsearch
|
|
686
|
+
spec:
|
|
687
|
+
replicas: 1
|
|
688
|
+
selector:
|
|
689
|
+
matchLabels:
|
|
690
|
+
app: elasticsearch
|
|
691
|
+
template:
|
|
692
|
+
metadata:
|
|
693
|
+
labels:
|
|
694
|
+
app: elasticsearch
|
|
695
|
+
spec:
|
|
696
|
+
containers:
|
|
697
|
+
- name: elasticsearch
|
|
698
|
+
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.2
|
|
699
|
+
env:
|
|
700
|
+
- name: discovery.type
|
|
701
|
+
value: single-node
|
|
702
|
+
- name: xpack.security.enabled
|
|
703
|
+
value: "false"
|
|
704
|
+
- name: ES_JAVA_OPTS
|
|
705
|
+
value: "-Xms512m -Xmx512m"
|
|
706
|
+
ports:
|
|
707
|
+
- containerPort: 9200
|
|
708
|
+
---
|
|
709
|
+
apiVersion: v1
|
|
710
|
+
kind: Service
|
|
711
|
+
metadata:
|
|
712
|
+
name: elasticsearch
|
|
713
|
+
spec:
|
|
714
|
+
ports:
|
|
715
|
+
- port: 9200
|
|
716
|
+
selector:
|
|
717
|
+
app: elasticsearch
|
|
718
|
+
`;
|
|
719
|
+
|
|
720
|
+
const k8sJaegerYaml = `apiVersion: apps/v1
|
|
721
|
+
kind: Deployment
|
|
722
|
+
metadata:
|
|
723
|
+
name: jaeger
|
|
724
|
+
labels:
|
|
725
|
+
app: jaeger
|
|
726
|
+
spec:
|
|
727
|
+
replicas: 1
|
|
728
|
+
selector:
|
|
729
|
+
matchLabels:
|
|
730
|
+
app: jaeger
|
|
731
|
+
template:
|
|
732
|
+
metadata:
|
|
733
|
+
labels:
|
|
734
|
+
app: jaeger
|
|
735
|
+
spec:
|
|
736
|
+
containers:
|
|
737
|
+
- name: jaeger
|
|
738
|
+
image: jaegertracing/all-in-one:1.55
|
|
739
|
+
ports:
|
|
740
|
+
- containerPort: 16686
|
|
741
|
+
- containerPort: 4317
|
|
742
|
+
env:
|
|
743
|
+
- name: COLLECTOR_OTLP_ENABLED
|
|
744
|
+
value: "true"
|
|
745
|
+
---
|
|
746
|
+
apiVersion: v1
|
|
747
|
+
kind: Service
|
|
748
|
+
metadata:
|
|
749
|
+
name: jaeger
|
|
750
|
+
spec:
|
|
751
|
+
ports:
|
|
752
|
+
- name: ui
|
|
753
|
+
port: 16686
|
|
754
|
+
- name: otlp-grpc
|
|
755
|
+
port: 4317
|
|
756
|
+
selector:
|
|
757
|
+
app: jaeger
|
|
758
|
+
`;
|
|
759
|
+
|
|
760
|
+
const k8sOtelCollectorYaml = `apiVersion: v1
|
|
761
|
+
kind: ConfigMap
|
|
762
|
+
metadata:
|
|
763
|
+
name: otel-collector-config
|
|
764
|
+
data:
|
|
765
|
+
otel-collector.yaml: |
|
|
766
|
+
receivers:
|
|
767
|
+
otlp:
|
|
768
|
+
protocols:
|
|
769
|
+
grpc:
|
|
770
|
+
http:
|
|
771
|
+
processors:
|
|
772
|
+
batch:
|
|
773
|
+
resourcedetection:
|
|
774
|
+
detectors: [env, system]
|
|
775
|
+
exporters:
|
|
776
|
+
logging:
|
|
777
|
+
loglevel: debug
|
|
778
|
+
otlp:
|
|
779
|
+
endpoint: "jaeger:4317"
|
|
780
|
+
tls:
|
|
781
|
+
insecure: true
|
|
782
|
+
service:
|
|
783
|
+
pipelines:
|
|
784
|
+
traces:
|
|
785
|
+
receivers: [otlp]
|
|
786
|
+
processors: [batch, resourcedetection]
|
|
787
|
+
exporters: [logging, otlp]
|
|
788
|
+
---
|
|
789
|
+
apiVersion: apps/v1
|
|
790
|
+
kind: Deployment
|
|
791
|
+
metadata:
|
|
792
|
+
name: otel-collector
|
|
793
|
+
labels:
|
|
794
|
+
app: otel-collector
|
|
795
|
+
spec:
|
|
796
|
+
replicas: 1
|
|
797
|
+
selector:
|
|
798
|
+
matchLabels:
|
|
799
|
+
app: otel-collector
|
|
800
|
+
template:
|
|
801
|
+
metadata:
|
|
802
|
+
labels:
|
|
803
|
+
app: otel-collector
|
|
804
|
+
spec:
|
|
805
|
+
containers:
|
|
806
|
+
- name: otel-collector
|
|
807
|
+
image: otel/opentelemetry-collector-contrib:0.96.0
|
|
808
|
+
command:
|
|
809
|
+
- --config=/etc/otel-collector-config.yaml
|
|
810
|
+
ports:
|
|
811
|
+
- containerPort: 4317
|
|
812
|
+
- containerPort: 4318
|
|
813
|
+
volumeMounts:
|
|
814
|
+
- name: config-volume
|
|
815
|
+
mountPath: /etc/otel-collector-config.yaml
|
|
816
|
+
subPath: otel-collector.yaml
|
|
817
|
+
volumes:
|
|
818
|
+
- name: config-volume
|
|
819
|
+
configMap:
|
|
820
|
+
name: otel-collector-config
|
|
821
|
+
---
|
|
822
|
+
apiVersion: v1
|
|
823
|
+
kind: Service
|
|
824
|
+
metadata:
|
|
825
|
+
name: otel-collector
|
|
826
|
+
spec:
|
|
827
|
+
ports:
|
|
828
|
+
- name: otlp-grpc
|
|
829
|
+
port: 4317
|
|
830
|
+
- name: otlp-http
|
|
831
|
+
port: 4318
|
|
832
|
+
selector:
|
|
833
|
+
app: otel-collector
|
|
834
|
+
`;
|
|
835
|
+
|
|
836
|
+
const k8sKeycloakYaml = `apiVersion: v1
|
|
837
|
+
kind: ConfigMap
|
|
838
|
+
metadata:
|
|
839
|
+
name: keycloak-realm-config
|
|
840
|
+
data:
|
|
841
|
+
realm.json: |
|
|
842
|
+
${realmJson.split('\n').map(l => ' ' + l).join('\n')}
|
|
843
|
+
---
|
|
844
|
+
apiVersion: apps/v1
|
|
845
|
+
kind: Deployment
|
|
846
|
+
metadata:
|
|
847
|
+
name: keycloak
|
|
848
|
+
labels:
|
|
849
|
+
app: keycloak
|
|
850
|
+
spec:
|
|
851
|
+
replicas: 1
|
|
852
|
+
selector:
|
|
853
|
+
matchLabels:
|
|
854
|
+
app: keycloak
|
|
855
|
+
template:
|
|
856
|
+
metadata:
|
|
857
|
+
labels:
|
|
858
|
+
app: keycloak
|
|
859
|
+
spec:
|
|
860
|
+
containers:
|
|
861
|
+
- name: keycloak
|
|
862
|
+
image: quay.io/keycloak/keycloak:26.2.3
|
|
863
|
+
args: ["start-dev", "--import-realm"]
|
|
864
|
+
env:
|
|
865
|
+
- name: KEYCLOAK_ADMIN
|
|
866
|
+
value: admin
|
|
867
|
+
- name: KEYCLOAK_ADMIN_PASSWORD
|
|
868
|
+
value: admin
|
|
869
|
+
- name: KC_DB
|
|
870
|
+
value: postgres
|
|
871
|
+
- name: KC_DB_URL
|
|
872
|
+
value: jdbc:postgresql://postgres:5432/keycloak
|
|
873
|
+
- name: KC_DB_USERNAME
|
|
874
|
+
value: "${config.datasource?.username || 'postgres'}"
|
|
875
|
+
- name: KC_DB_PASSWORD
|
|
876
|
+
value: "${config.datasource?.password || 'password'}"
|
|
877
|
+
ports:
|
|
878
|
+
- containerPort: 8080
|
|
879
|
+
volumeMounts:
|
|
880
|
+
- name: realm-volume
|
|
881
|
+
mountPath: /opt/keycloak/data/import/realm.json
|
|
882
|
+
subPath: realm.json
|
|
883
|
+
volumes:
|
|
884
|
+
- name: realm-volume
|
|
885
|
+
configMap:
|
|
886
|
+
name: keycloak-realm-config
|
|
887
|
+
---
|
|
888
|
+
apiVersion: v1
|
|
889
|
+
kind: Service
|
|
890
|
+
metadata:
|
|
891
|
+
name: keycloak
|
|
892
|
+
spec:
|
|
893
|
+
ports:
|
|
894
|
+
- port: 8080
|
|
895
|
+
selector:
|
|
896
|
+
app: keycloak
|
|
897
|
+
`;
|
|
898
|
+
|
|
403
899
|
const realmConfigDir = path.join(devopsDir, 'keycloak', 'realm-config');
|
|
404
900
|
await fs.ensureDir(realmConfigDir);
|
|
405
901
|
await fs.writeFile(path.join(realmConfigDir, `${realmName}-realm.json`), realmJson);
|
|
@@ -413,8 +909,102 @@ jobs:
|
|
|
413
909
|
await fs.writeFile(path.join(devopsDir, 'app.yml'), appYaml);
|
|
414
910
|
await fs.writeFile(path.join(devopsDir, 'docker-compose.yml'), dockerCompose);
|
|
415
911
|
await fs.writeFile(path.join(k8sDir, 'mosquitto.conf'), mosquittoConf);
|
|
912
|
+
|
|
913
|
+
// Write Kubernetes manifest assets
|
|
914
|
+
await fs.writeFile(path.join(k8sDir, 'app.yaml'), k8sAppYaml);
|
|
915
|
+
await fs.writeFile(path.join(k8sDir, 'postgres.yaml'), k8sPostgresYaml);
|
|
916
|
+
await fs.writeFile(path.join(k8sDir, 'redis.yaml'), k8sRedisYaml);
|
|
917
|
+
await fs.writeFile(path.join(k8sDir, 'nats.yaml'), k8sNatsYaml);
|
|
918
|
+
await fs.writeFile(path.join(k8sDir, 'mosquitto.yaml'), k8sMosquittoYaml);
|
|
919
|
+
await fs.writeFile(path.join(k8sDir, 'elasticsearch.yaml'), k8sElasticsearchYaml);
|
|
920
|
+
await fs.writeFile(path.join(k8sDir, 'jaeger.yaml'), k8sJaegerYaml);
|
|
921
|
+
await fs.writeFile(path.join(k8sDir, 'otel-collector-k8s.yaml'), k8sOtelCollectorYaml);
|
|
922
|
+
await fs.writeFile(path.join(k8sDir, 'keycloak.yaml'), k8sKeycloakYaml);
|
|
923
|
+
|
|
924
|
+
// Additional optional services manifests
|
|
925
|
+
const k8sMongoYaml = `apiVersion: apps/v1
|
|
926
|
+
kind: Deployment
|
|
927
|
+
metadata:
|
|
928
|
+
name: mongodb
|
|
929
|
+
labels:
|
|
930
|
+
app: mongodb
|
|
931
|
+
spec:
|
|
932
|
+
replicas: 1
|
|
933
|
+
selector:
|
|
934
|
+
matchLabels:
|
|
935
|
+
app: mongodb
|
|
936
|
+
template:
|
|
937
|
+
metadata:
|
|
938
|
+
labels:
|
|
939
|
+
app: mongodb
|
|
940
|
+
spec:
|
|
941
|
+
containers:
|
|
942
|
+
- name: mongodb
|
|
943
|
+
image: mongo:7.0.5
|
|
944
|
+
ports:
|
|
945
|
+
- containerPort: 27017
|
|
946
|
+
---
|
|
947
|
+
apiVersion: v1
|
|
948
|
+
kind: Service
|
|
949
|
+
metadata:
|
|
950
|
+
name: mongodb
|
|
951
|
+
spec:
|
|
952
|
+
ports:
|
|
953
|
+
- port: 27017
|
|
954
|
+
selector:
|
|
955
|
+
app: mongodb`;
|
|
956
|
+
const k8sMinioYaml = `apiVersion: apps/v1
|
|
957
|
+
kind: Deployment
|
|
958
|
+
metadata:
|
|
959
|
+
name: minio
|
|
960
|
+
labels:
|
|
961
|
+
app: minio
|
|
962
|
+
spec:
|
|
963
|
+
replicas: 1
|
|
964
|
+
selector:
|
|
965
|
+
matchLabels:
|
|
966
|
+
app: minio
|
|
967
|
+
template:
|
|
968
|
+
metadata:
|
|
969
|
+
labels:
|
|
970
|
+
app: minio
|
|
971
|
+
spec:
|
|
972
|
+
containers:
|
|
973
|
+
- name: minio
|
|
974
|
+
image: minio/minio:RELEASE.2024-05-10T19-05-27Z
|
|
975
|
+
args:
|
|
976
|
+
- server
|
|
977
|
+
- /data
|
|
978
|
+
env:
|
|
979
|
+
- name: MINIO_ROOT_USER
|
|
980
|
+
value: "minioadmin"
|
|
981
|
+
- name: MINIO_ROOT_PASSWORD
|
|
982
|
+
value: "minioadmin"
|
|
983
|
+
ports:
|
|
984
|
+
- containerPort: 9000
|
|
985
|
+
---
|
|
986
|
+
apiVersion: v1
|
|
987
|
+
kind: Service
|
|
988
|
+
metadata:
|
|
989
|
+
name: minio
|
|
990
|
+
spec:
|
|
991
|
+
ports:
|
|
992
|
+
- port: 9000
|
|
993
|
+
selector:
|
|
994
|
+
app: minio`;
|
|
995
|
+
await fs.writeFile(path.join(k8sDir, 'mongodb.yaml'), k8sMongoYaml);
|
|
996
|
+
await fs.writeFile(path.join(k8sDir, 'minio.yaml'), k8sMinioYaml);
|
|
997
|
+
|
|
416
998
|
await fs.writeFile(path.join(githubDir, 'ci.yml'), ciWorkflow);
|
|
417
999
|
await fs.writeFile(path.join(githubDir, 'cd.yml'), cdWorkflow);
|
|
418
1000
|
|
|
419
|
-
|
|
1001
|
+
const pushScriptPath = path.join(projectRootDir, 'push.sh');
|
|
1002
|
+
await fs.writeFile(pushScriptPath, pushScript);
|
|
1003
|
+
try {
|
|
1004
|
+
await fs.chmod(pushScriptPath, 0o755);
|
|
1005
|
+
} catch (e) {
|
|
1006
|
+
// Safe fallback for OS environments where chmod is not supported
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
console.log(chalk.gray(' Generated devops/Dockerfile, devops/services.yml, devops/app.yml, devops/docker-compose.yml, push.sh, Kubernetes manifests & GitHub Actions CI/CD'));
|
|
420
1010
|
};
|