@hed-hog/cli 0.0.55 → 0.0.59
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/dist/package.json +1 -1
- package/dist/src/modules/database/database.service.d.ts +1 -0
- package/dist/src/modules/database/database.service.js +30 -10
- package/dist/src/modules/database/database.service.js.map +1 -1
- package/dist/src/modules/developer/developer.service.d.ts +5 -0
- package/dist/src/modules/developer/developer.service.js +186 -21
- package/dist/src/modules/developer/developer.service.js.map +1 -1
- package/dist/src/modules/hedhog/hedhog.service.d.ts +1 -0
- package/dist/src/modules/hedhog/hedhog.service.js +48 -0
- package/dist/src/modules/hedhog/hedhog.service.js.map +1 -1
- package/dist/src/templates/deployment/DEPLOYMENT.md.ejs +9 -1
- package/dist/src/templates/deployment/k8s.configmap.yaml.ejs +11 -0
- package/dist/src/templates/deployment/k8s.deployment.yaml.ejs +33 -10
- package/dist/src/templates/deployment/k8s.postgres.readme.ms.ejs +172 -0
- package/dist/src/templates/deployment/k8s.postgres.service.yaml.ejs +34 -0
- package/dist/src/templates/deployment/k8s.postgres.statefulset.yaml.ejs +100 -0
- package/dist/src/templates/deployment/workflow.deploy.yml.ejs +190 -25
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -6,9 +6,16 @@ on:
|
|
|
6
6
|
workflow_dispatch:
|
|
7
7
|
env:
|
|
8
8
|
REGISTRY: <%= config.containerRegistry %>
|
|
9
|
-
CLUSTER_NAME: <%= config.clusterName %>
|
|
10
9
|
NAMESPACE: <%= config.namespace %>
|
|
11
|
-
|
|
10
|
+
K8S_CLUSTER_ID: <%= config.k8sClusterId || config.clusterName %>
|
|
11
|
+
# API environment
|
|
12
|
+
API_FRONTEND_URL: <%= config.frontendUrl || (config.domain ? `https://${config.domain}` : '') %>
|
|
13
|
+
API_JWT_EXPIRES_IN: <%= config.jwtExpiresIn || '7d' %>
|
|
14
|
+
# Admin environment (runtime-replaced at container startup)
|
|
15
|
+
ADMIN_API_BASE_URL: <%= config.domain ? `https://api.${config.domain}` : '' %>
|
|
16
|
+
ADMIN_API_URL: <%= config.domain ? `https://api.${config.domain}` : '' %>
|
|
17
|
+
ADMIN_INTERNAL_API_URL: http://<%= config.appName %>-api:<%= config.appPorts?.api || 3000 %>
|
|
18
|
+
FRONTEND_URLS: <%= config.frontendUrls || (config.domain ? `https://${config.domain}` : '') %>
|
|
12
19
|
jobs:
|
|
13
20
|
apply-cluster-config:
|
|
14
21
|
name: Apply Kubernetes and Helm configs
|
|
@@ -20,10 +27,8 @@ jobs:
|
|
|
20
27
|
uses: digitalocean/action-doctl@v2
|
|
21
28
|
with:
|
|
22
29
|
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
|
|
23
|
-
- name: Set up Helm
|
|
24
|
-
uses: azure/setup-helm@v4
|
|
25
30
|
- name: Save DigitalOcean kubeconfig
|
|
26
|
-
run: doctl kubernetes cluster kubeconfig save ${{ env.
|
|
31
|
+
run: doctl kubernetes cluster kubeconfig save ${{ env.K8S_CLUSTER_ID }}
|
|
27
32
|
- name: Ensure namespace exists
|
|
28
33
|
run: |
|
|
29
34
|
kubectl create namespace ${{ env.NAMESPACE }} --dry-run=client -o yaml | kubectl apply -f -
|
|
@@ -32,12 +37,7 @@ jobs:
|
|
|
32
37
|
<% if (config.setupSSL) { %> kubectl apply -f k8s/cert-manager-issuer.yaml
|
|
33
38
|
<% } %><% (config.apps || []).forEach((app) => { %> kubectl apply -f k8s/<%= app %> -n ${{ env.NAMESPACE }}
|
|
34
39
|
<% }); %><% if (config.setupIngress) { %> kubectl apply -f k8s/ingress.yaml -n ${{ env.NAMESPACE }}
|
|
35
|
-
<% } %><% if ((config.infraServices || []).length > 0) { %>
|
|
36
|
-
run: |
|
|
37
|
-
<% config.infraServices.forEach((service) => { %> helm upgrade --install <%= config.appName %>-<%= service.name %> ./helm/services/<%= service.name %> \
|
|
38
|
-
--namespace ${{ env.NAMESPACE }} \
|
|
39
|
-
--create-namespace
|
|
40
|
-
<% }); %><% } %>
|
|
40
|
+
<% } %><% if ((config.infraServices || []).length > 0) { %>
|
|
41
41
|
|
|
42
42
|
<% if (config.apps.includes('api')) { %> deploy-api:
|
|
43
43
|
name: Deploy API
|
|
@@ -50,24 +50,118 @@ jobs:
|
|
|
50
50
|
uses: digitalocean/action-doctl@v2
|
|
51
51
|
with:
|
|
52
52
|
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
|
|
53
|
+
- name: Validate DigitalOcean token
|
|
54
|
+
run: |
|
|
55
|
+
doctl auth validate
|
|
56
|
+
doctl account get
|
|
57
|
+
- name: Validate Container Registry access
|
|
58
|
+
run: |
|
|
59
|
+
doctl registry get
|
|
60
|
+
doctl registry repository list >/dev/null 2>&1 && doctl registry repository list || echo "doctl registry repository list not available in this version"
|
|
53
61
|
- name: Log in to Container Registry
|
|
54
|
-
run: doctl registry login
|
|
55
|
-
- name:
|
|
62
|
+
run: doctl registry login --expiry-seconds 1200
|
|
63
|
+
- name: Check Docker auth config
|
|
64
|
+
run: |
|
|
65
|
+
if [ -f "$HOME/.docker/config.json" ]; then
|
|
66
|
+
grep -q 'registry.digitalocean.com' "$HOME/.docker/config.json" && echo "Docker auth entry for registry.digitalocean.com found"
|
|
67
|
+
else
|
|
68
|
+
echo "Docker config not found at $HOME/.docker/config.json"
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
- name: Registry auth probe push
|
|
72
|
+
run: |
|
|
73
|
+
docker pull hello-world
|
|
74
|
+
docker tag hello-world ${{ env.REGISTRY }}/ci-auth-probe:${{ github.run_id }}
|
|
75
|
+
docker push ${{ env.REGISTRY }}/ci-auth-probe:${{ github.run_id }}
|
|
76
|
+
- name: Build Docker image
|
|
56
77
|
run: |
|
|
57
78
|
docker build -t ${{ env.REGISTRY }}/<%= config.appName %>-api:${{ github.sha }} \
|
|
58
79
|
-f apps/api/Dockerfile .
|
|
80
|
+
- name: Push Docker image (sha)
|
|
81
|
+
run: |
|
|
59
82
|
docker push ${{ env.REGISTRY }}/<%= config.appName %>-api:${{ github.sha }}
|
|
83
|
+
- name: Push Docker image (latest)
|
|
84
|
+
run: |
|
|
60
85
|
docker tag ${{ env.REGISTRY }}/<%= config.appName %>-api:${{ github.sha }} \
|
|
61
86
|
${{ env.REGISTRY }}/<%= config.appName %>-api:latest
|
|
62
87
|
docker push ${{ env.REGISTRY }}/<%= config.appName %>-api:latest
|
|
88
|
+
- name: Build and push migrator image
|
|
89
|
+
run: |
|
|
90
|
+
docker build --target migrator \
|
|
91
|
+
-t ${{ env.REGISTRY }}/<%= config.appName %>-api-migrate:${{ github.sha }} \
|
|
92
|
+
-f apps/api/Dockerfile .
|
|
93
|
+
docker push ${{ env.REGISTRY }}/<%= config.appName %>-api-migrate:${{ github.sha }}
|
|
63
94
|
- name: Save DigitalOcean kubeconfig
|
|
64
|
-
run: doctl kubernetes cluster kubeconfig save ${{ env.
|
|
95
|
+
run: doctl kubernetes cluster kubeconfig save ${{ env.K8S_CLUSTER_ID }}
|
|
96
|
+
- name: Validate required GitHub secrets
|
|
97
|
+
env:
|
|
98
|
+
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
|
99
|
+
run: |
|
|
100
|
+
if [ -z "$DATABASE_URL" ]; then
|
|
101
|
+
echo "::error::GitHub secret DATABASE_URL is not set. Configure it in Settings > Secrets and variables > Actions."
|
|
102
|
+
echo "::error::Expected format: postgresql://postgres:<password>@postgresql-<%= config.appName %>:5432/<%= config.appName %>"
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
- name: Ensure API config exists
|
|
106
|
+
run: |
|
|
107
|
+
kubectl create configmap <%= config.appName %>-api-config \
|
|
108
|
+
-n ${{ env.NAMESPACE }} \
|
|
109
|
+
--from-literal=FRONTEND_URL='${{ env.API_FRONTEND_URL }}' \
|
|
110
|
+
--from-literal=JWT_EXPIRES_IN='${{ env.API_JWT_EXPIRES_IN }}' \
|
|
111
|
+
--dry-run=client -o yaml | kubectl apply -f -
|
|
112
|
+
- name: Ensure API secrets exist
|
|
113
|
+
run: |
|
|
114
|
+
kubectl create secret generic <%= config.appName %>-api-secrets \
|
|
115
|
+
-n ${{ env.NAMESPACE }} \
|
|
116
|
+
--from-literal=DATABASE_URL='${{ secrets.DATABASE_URL }}' \
|
|
117
|
+
--from-literal=JWT_SECRET='${{ secrets.JWT_SECRET }}' \
|
|
118
|
+
--from-literal=ENCRYPTION_SECRET='${{ secrets.ENCRYPTION_SECRET }}' \
|
|
119
|
+
--from-literal=PEPPER='${{ secrets.PEPPER }}' \
|
|
120
|
+
--dry-run=client -o yaml | kubectl apply -f -
|
|
121
|
+
- name: Run database migrations
|
|
122
|
+
run: |
|
|
123
|
+
kubectl delete job <%= config.appName %>-api-migrate -n ${{ env.NAMESPACE }} --ignore-not-found
|
|
124
|
+
cat <<EOF | kubectl apply -f -
|
|
125
|
+
apiVersion: batch/v1
|
|
126
|
+
kind: Job
|
|
127
|
+
metadata:
|
|
128
|
+
name: <%= config.appName %>-api-migrate
|
|
129
|
+
namespace: ${{ env.NAMESPACE }}
|
|
130
|
+
spec:
|
|
131
|
+
backoffLimit: 3
|
|
132
|
+
ttlSecondsAfterFinished: 300
|
|
133
|
+
template:
|
|
134
|
+
spec:
|
|
135
|
+
restartPolicy: Never
|
|
136
|
+
containers:
|
|
137
|
+
- name: migrate
|
|
138
|
+
image: ${{ env.REGISTRY }}/<%= config.appName %>-api-migrate:${{ github.sha }}
|
|
139
|
+
env:
|
|
140
|
+
- name: DATABASE_URL
|
|
141
|
+
valueFrom:
|
|
142
|
+
secretKeyRef:
|
|
143
|
+
name: <%= config.appName %>-api-secrets
|
|
144
|
+
key: DATABASE_URL
|
|
145
|
+
EOF
|
|
146
|
+
kubectl wait --for=condition=complete job/<%= config.appName %>-api-migrate \
|
|
147
|
+
-n ${{ env.NAMESPACE }} --timeout=120s || (
|
|
148
|
+
echo "--- Migration failed. Logs:"
|
|
149
|
+
kubectl logs job/<%= config.appName %>-api-migrate -n ${{ env.NAMESPACE }} --tail=50
|
|
150
|
+
exit 1
|
|
151
|
+
)
|
|
65
152
|
- name: Deploy to Kubernetes
|
|
66
153
|
run: |
|
|
67
154
|
kubectl set image deployment/<%= config.appName %>-api \
|
|
68
155
|
<%= config.appName %>-api=${{ env.REGISTRY }}/<%= config.appName %>-api:${{ github.sha }} \
|
|
69
156
|
-n ${{ env.NAMESPACE }}
|
|
70
|
-
kubectl rollout status deployment/<%= config.appName %>-api -n ${{ env.NAMESPACE }}
|
|
157
|
+
kubectl rollout status deployment/<%= config.appName %>-api -n ${{ env.NAMESPACE }} --timeout=5m || (
|
|
158
|
+
echo "--- Rollout timed out. Pod status:"
|
|
159
|
+
kubectl get pods -l app=<%= config.appName %>-api -n ${{ env.NAMESPACE }}
|
|
160
|
+
echo "--- Recent events:"
|
|
161
|
+
kubectl describe deployment/<%= config.appName %>-api -n ${{ env.NAMESPACE }} | tail -20
|
|
162
|
+
kubectl logs -l app=<%= config.appName %>-api -n ${{ env.NAMESPACE }} --tail=50 --previous 2>/dev/null || true
|
|
163
|
+
exit 1
|
|
164
|
+
)
|
|
71
165
|
|
|
72
166
|
<% } %><% if (config.apps.includes('admin')) { %> deploy-admin:
|
|
73
167
|
name: Deploy ADMIN
|
|
@@ -80,26 +174,65 @@ jobs:
|
|
|
80
174
|
uses: digitalocean/action-doctl@v2
|
|
81
175
|
with:
|
|
82
176
|
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
|
|
177
|
+
- name: Validate DigitalOcean token
|
|
178
|
+
run: |
|
|
179
|
+
doctl auth validate
|
|
180
|
+
doctl account get
|
|
181
|
+
- name: Validate Container Registry access
|
|
182
|
+
run: |
|
|
183
|
+
doctl registry get
|
|
184
|
+
doctl registry repository list >/dev/null 2>&1 && doctl registry repository list || echo "doctl registry repository list not available in this version"
|
|
83
185
|
- name: Log in to Container Registry
|
|
84
|
-
run: doctl registry login
|
|
85
|
-
- name:
|
|
186
|
+
run: doctl registry login --expiry-seconds 1200
|
|
187
|
+
- name: Check Docker auth config
|
|
188
|
+
run: |
|
|
189
|
+
if [ -f "$HOME/.docker/config.json" ]; then
|
|
190
|
+
grep -q 'registry.digitalocean.com' "$HOME/.docker/config.json" && echo "Docker auth entry for registry.digitalocean.com found"
|
|
191
|
+
else
|
|
192
|
+
echo "Docker config not found at $HOME/.docker/config.json"
|
|
193
|
+
exit 1
|
|
194
|
+
fi
|
|
195
|
+
- name: Registry auth probe push
|
|
196
|
+
run: |
|
|
197
|
+
docker pull hello-world
|
|
198
|
+
docker tag hello-world ${{ env.REGISTRY }}/ci-auth-probe-admin:${{ github.run_id }}
|
|
199
|
+
docker push ${{ env.REGISTRY }}/ci-auth-probe-admin:${{ github.run_id }}
|
|
200
|
+
- name: Build Docker image
|
|
86
201
|
run: |
|
|
87
202
|
docker build -t ${{ env.REGISTRY }}/<%= config.appName %>-admin:${{ github.sha }} \
|
|
88
|
-
--build-arg NEXT_PUBLIC_API_BASE_URL=${{ env.API_PUBLIC_URL }} \
|
|
89
|
-
--build-arg NEXT_PUBLIC_API_URL=${{ env.API_PUBLIC_URL }} \
|
|
90
203
|
-f apps/admin/Dockerfile .
|
|
204
|
+
- name: Push Docker image (sha)
|
|
205
|
+
run: |
|
|
91
206
|
docker push ${{ env.REGISTRY }}/<%= config.appName %>-admin:${{ github.sha }}
|
|
207
|
+
- name: Push Docker image (latest)
|
|
208
|
+
run: |
|
|
92
209
|
docker tag ${{ env.REGISTRY }}/<%= config.appName %>-admin:${{ github.sha }} \
|
|
93
210
|
${{ env.REGISTRY }}/<%= config.appName %>-admin:latest
|
|
94
211
|
docker push ${{ env.REGISTRY }}/<%= config.appName %>-admin:latest
|
|
95
212
|
- name: Save DigitalOcean kubeconfig
|
|
96
|
-
run: doctl kubernetes cluster kubeconfig save ${{ env.
|
|
213
|
+
run: doctl kubernetes cluster kubeconfig save ${{ env.K8S_CLUSTER_ID }}
|
|
214
|
+
- name: Ensure admin config exists
|
|
215
|
+
run: |
|
|
216
|
+
kubectl create configmap <%= config.appName %>-admin-config \
|
|
217
|
+
-n ${{ env.NAMESPACE }} \
|
|
218
|
+
--from-literal=NEXT_PUBLIC_API_BASE_URL='${{ env.ADMIN_API_BASE_URL }}' \
|
|
219
|
+
--from-literal=NEXT_PUBLIC_API_URL='${{ env.ADMIN_API_URL }}' \
|
|
220
|
+
--from-literal=INTERNAL_API_URL='${{ env.ADMIN_INTERNAL_API_URL }}' \
|
|
221
|
+
--from-literal=FRONTEND_URLS='${{ env.FRONTEND_URLS }}' \
|
|
222
|
+
--dry-run=client -o yaml | kubectl apply -f -
|
|
97
223
|
- name: Deploy to Kubernetes
|
|
98
224
|
run: |
|
|
99
225
|
kubectl set image deployment/<%= config.appName %>-admin \
|
|
100
226
|
<%= config.appName %>-admin=${{ env.REGISTRY }}/<%= config.appName %>-admin:${{ github.sha }} \
|
|
101
227
|
-n ${{ env.NAMESPACE }}
|
|
102
|
-
kubectl rollout status deployment/<%= config.appName %>-admin -n ${{ env.NAMESPACE }}
|
|
228
|
+
kubectl rollout status deployment/<%= config.appName %>-admin -n ${{ env.NAMESPACE }} --timeout=5m || (
|
|
229
|
+
echo "--- Rollout timed out. Pod status:"
|
|
230
|
+
kubectl get pods -l app=<%= config.appName %>-admin -n ${{ env.NAMESPACE }}
|
|
231
|
+
echo "--- Recent events:"
|
|
232
|
+
kubectl describe deployment/<%= config.appName %>-admin -n ${{ env.NAMESPACE }} | tail -20
|
|
233
|
+
kubectl logs -l app=<%= config.appName %>-admin -n ${{ env.NAMESPACE }} --tail=50 --previous 2>/dev/null || true
|
|
234
|
+
exit 1
|
|
235
|
+
)
|
|
103
236
|
<% } %><% (config.apps || []).filter((app) => app !== 'api' && app !== 'admin').forEach((app) => { %>
|
|
104
237
|
deploy-<%= app %>:
|
|
105
238
|
name: Deploy <%= app.toUpperCase() %>
|
|
@@ -112,22 +245,54 @@ jobs:
|
|
|
112
245
|
uses: digitalocean/action-doctl@v2
|
|
113
246
|
with:
|
|
114
247
|
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
|
|
248
|
+
- name: Validate DigitalOcean token
|
|
249
|
+
run: |
|
|
250
|
+
doctl auth validate
|
|
251
|
+
doctl account get
|
|
252
|
+
- name: Validate Container Registry access
|
|
253
|
+
run: |
|
|
254
|
+
doctl registry get
|
|
255
|
+
doctl registry repository list >/dev/null 2>&1 && doctl registry repository list || echo "doctl registry repository list not available in this version"
|
|
115
256
|
- name: Log in to Container Registry
|
|
116
|
-
run: doctl registry login
|
|
117
|
-
- name:
|
|
257
|
+
run: doctl registry login --expiry-seconds 1200
|
|
258
|
+
- name: Check Docker auth config
|
|
259
|
+
run: |
|
|
260
|
+
if [ -f "$HOME/.docker/config.json" ]; then
|
|
261
|
+
grep -q 'registry.digitalocean.com' "$HOME/.docker/config.json" && echo "Docker auth entry for registry.digitalocean.com found"
|
|
262
|
+
else
|
|
263
|
+
echo "Docker config not found at $HOME/.docker/config.json"
|
|
264
|
+
exit 1
|
|
265
|
+
fi
|
|
266
|
+
- name: Registry auth probe push
|
|
267
|
+
run: |
|
|
268
|
+
docker pull hello-world
|
|
269
|
+
docker tag hello-world ${{ env.REGISTRY }}/ci-auth-probe-<%= app %>:${{ github.run_id }}
|
|
270
|
+
docker push ${{ env.REGISTRY }}/ci-auth-probe-<%= app %>:${{ github.run_id }}
|
|
271
|
+
- name: Build Docker image
|
|
118
272
|
run: |
|
|
119
273
|
docker build -t ${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:${{ github.sha }} \
|
|
120
274
|
-f apps/<%= app %>/Dockerfile .
|
|
275
|
+
- name: Push Docker image (sha)
|
|
276
|
+
run: |
|
|
121
277
|
docker push ${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:${{ github.sha }}
|
|
278
|
+
- name: Push Docker image (latest)
|
|
279
|
+
run: |
|
|
122
280
|
docker tag ${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:${{ github.sha }} \
|
|
123
281
|
${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:latest
|
|
124
282
|
docker push ${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:latest
|
|
125
283
|
- name: Save DigitalOcean kubeconfig
|
|
126
|
-
run: doctl kubernetes cluster kubeconfig save ${{ env.
|
|
284
|
+
run: doctl kubernetes cluster kubeconfig save ${{ env.K8S_CLUSTER_ID }}
|
|
127
285
|
- name: Deploy to Kubernetes
|
|
128
286
|
run: |
|
|
129
287
|
kubectl set image deployment/<%= config.appName %>-<%= app %> \
|
|
130
288
|
<%= config.appName %>-<%= app %>=${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:${{ github.sha }} \
|
|
131
289
|
-n ${{ env.NAMESPACE }}
|
|
132
|
-
kubectl rollout status deployment/<%= config.appName %>-<%= app %> -n ${{ env.NAMESPACE }}
|
|
290
|
+
kubectl rollout status deployment/<%= config.appName %>-<%= app %> -n ${{ env.NAMESPACE }} --timeout=5m || (
|
|
291
|
+
echo "--- Rollout timed out. Pod status:"
|
|
292
|
+
kubectl get pods -l app=<%= config.appName %>-<%= app %> -n ${{ env.NAMESPACE }}
|
|
293
|
+
echo "--- Recent events:"
|
|
294
|
+
kubectl describe deployment/<%= config.appName %>-<%= app %> -n ${{ env.NAMESPACE }} | tail -20
|
|
295
|
+
kubectl logs -l app=<%= config.appName %>-<%= app %> -n ${{ env.NAMESPACE }} --tail=50 --previous 2>/dev/null || true
|
|
296
|
+
exit 1
|
|
297
|
+
)
|
|
133
298
|
<% }); %>
|