@hed-hog/cli 0.0.59 → 0.0.61
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/developer/developer.service.js +39 -1
- package/dist/src/modules/developer/developer.service.js.map +1 -1
- package/dist/src/templates/deployment/k8s.deployment.yaml.ejs +27 -3
- package/dist/src/templates/deployment/workflow.deploy.yml.ejs +55 -78
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -23,6 +23,23 @@ spec:
|
|
|
23
23
|
env:
|
|
24
24
|
- name: NODE_ENV
|
|
25
25
|
value: production
|
|
26
|
+
<% if (app === 'admin') { %>
|
|
27
|
+
- name: INTERNAL_API_URL
|
|
28
|
+
valueFrom:
|
|
29
|
+
configMapKeyRef:
|
|
30
|
+
name: <%= config.appName %>-<%= app %>-config
|
|
31
|
+
key: INTERNAL_API_URL
|
|
32
|
+
- name: NEXT_PUBLIC_API_BASE_URL
|
|
33
|
+
valueFrom:
|
|
34
|
+
configMapKeyRef:
|
|
35
|
+
name: <%= config.appName %>-<%= app %>-config
|
|
36
|
+
key: NEXT_PUBLIC_API_BASE_URL
|
|
37
|
+
- name: NEXT_PUBLIC_API_URL
|
|
38
|
+
valueFrom:
|
|
39
|
+
configMapKeyRef:
|
|
40
|
+
name: <%= config.appName %>-<%= app %>-config
|
|
41
|
+
key: NEXT_PUBLIC_API_URL
|
|
42
|
+
<% } else { %>
|
|
26
43
|
<%
|
|
27
44
|
const secretEnvKeys = new Set(['DATABASE_URL', 'ENCRYPTION_SECRET', 'JWT_SECRET', 'PEPPER']);
|
|
28
45
|
Object.keys(appEnv || {}).sort().filter((key) => key !== 'NODE_ENV').forEach((key) => {
|
|
@@ -37,6 +54,7 @@ Object.keys(appEnv || {}).sort().filter((key) => key !== 'NODE_ENV').forEach((ke
|
|
|
37
54
|
key: <%= key %>
|
|
38
55
|
<% } %>
|
|
39
56
|
<% }); %>
|
|
57
|
+
<% } %>
|
|
40
58
|
resources:
|
|
41
59
|
requests:
|
|
42
60
|
memory: 512Mi
|
|
@@ -46,22 +64,28 @@ Object.keys(appEnv || {}).sort().filter((key) => key !== 'NODE_ENV').forEach((ke
|
|
|
46
64
|
cpu: 500m
|
|
47
65
|
startupProbe:
|
|
48
66
|
httpGet:
|
|
49
|
-
path: <%= app === 'api' ? '/health' : '/' %>
|
|
67
|
+
path: <%= app === 'api' || app === 'admin' ? '/health' : '/' %>
|
|
50
68
|
port: <%= config.appPorts?.[app] ?? (app === 'api' ? 3000 : 80) %>
|
|
51
69
|
initialDelaySeconds: 15
|
|
52
70
|
periodSeconds: 10
|
|
53
71
|
failureThreshold: 12
|
|
72
|
+
<% if (app === 'admin') { %> timeoutSeconds: 5
|
|
73
|
+
<% } %>
|
|
54
74
|
livenessProbe:
|
|
55
75
|
httpGet:
|
|
56
|
-
path: <%= app === 'api' ? '/health' : '/' %>
|
|
76
|
+
path: <%= app === 'api' || app === 'admin' ? '/health' : '/' %>
|
|
57
77
|
port: <%= config.appPorts?.[app] ?? (app === 'api' ? 3000 : 80) %>
|
|
58
78
|
initialDelaySeconds: 0
|
|
59
79
|
periodSeconds: 15
|
|
60
80
|
failureThreshold: 3
|
|
81
|
+
<% if (app === 'admin') { %> timeoutSeconds: 5
|
|
82
|
+
<% } %>
|
|
61
83
|
readinessProbe:
|
|
62
84
|
httpGet:
|
|
63
|
-
path: <%= app === 'api' ? '/health' : '/' %>
|
|
85
|
+
path: <%= app === 'api' || app === 'admin' ? '/health' : '/' %>
|
|
64
86
|
port: <%= config.appPorts?.[app] ?? (app === 'api' ? 3000 : 80) %>
|
|
65
87
|
initialDelaySeconds: 0
|
|
66
88
|
periodSeconds: 10
|
|
67
89
|
failureThreshold: 3
|
|
90
|
+
<% if (app === 'admin') { %> timeoutSeconds: 5
|
|
91
|
+
<% } %>
|
|
@@ -2,20 +2,24 @@ name: Deploy to Kubernetes
|
|
|
2
2
|
on:
|
|
3
3
|
push:
|
|
4
4
|
branches:
|
|
5
|
-
- <%= config.deployBranch
|
|
5
|
+
- <%= config.deployBranch %>
|
|
6
6
|
workflow_dispatch:
|
|
7
7
|
env:
|
|
8
8
|
REGISTRY: <%= config.containerRegistry %>
|
|
9
9
|
NAMESPACE: <%= config.namespace %>
|
|
10
|
-
K8S_CLUSTER_ID: <%= config.
|
|
10
|
+
K8S_CLUSTER_ID: <%= config.clusterName %>
|
|
11
|
+
<% if ((config.apps || []).includes('api')) { %>
|
|
11
12
|
# API environment
|
|
12
|
-
API_FRONTEND_URL: <%= config.
|
|
13
|
+
API_FRONTEND_URL: <%= config.domain ? 'https://' + config.domain : '' %>
|
|
13
14
|
API_JWT_EXPIRES_IN: <%= config.jwtExpiresIn || '7d' %>
|
|
15
|
+
<% } %>
|
|
16
|
+
<% if ((config.apps || []).includes('admin')) { %>
|
|
14
17
|
# Admin environment (runtime-replaced at container startup)
|
|
15
|
-
ADMIN_API_BASE_URL: <%= config.
|
|
16
|
-
ADMIN_API_URL: <%= config.
|
|
17
|
-
ADMIN_INTERNAL_API_URL: http://<%= config.appName %>-api
|
|
18
|
-
FRONTEND_URLS: <%= config.frontendUrls
|
|
18
|
+
ADMIN_API_BASE_URL: <%= config.apiDomain || '' %>
|
|
19
|
+
ADMIN_API_URL: <%= config.apiDomain || '' %>
|
|
20
|
+
ADMIN_INTERNAL_API_URL: http://<%= config.appName %>-api:3100
|
|
21
|
+
FRONTEND_URLS: <%= config.frontendUrls %>
|
|
22
|
+
<% } %>
|
|
19
23
|
jobs:
|
|
20
24
|
apply-cluster-config:
|
|
21
25
|
name: Apply Kubernetes and Helm configs
|
|
@@ -34,12 +38,14 @@ jobs:
|
|
|
34
38
|
kubectl create namespace ${{ env.NAMESPACE }} --dry-run=client -o yaml | kubectl apply -f -
|
|
35
39
|
- name: Apply Kubernetes manifests
|
|
36
40
|
run: |
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
<% (config.apps || []).forEach(function(app) { %>
|
|
42
|
+
kubectl apply -f k8s/<%= app %> -n ${{ env.NAMESPACE }}
|
|
43
|
+
<% }); %>
|
|
44
|
+
|
|
41
45
|
|
|
42
|
-
|
|
46
|
+
|
|
47
|
+
<% if ((config.apps || []).includes('api')) { %>
|
|
48
|
+
deploy-api:
|
|
43
49
|
name: Deploy API
|
|
44
50
|
runs-on: ubuntu-latest
|
|
45
51
|
needs: apply-cluster-config
|
|
@@ -77,6 +83,16 @@ jobs:
|
|
|
77
83
|
run: |
|
|
78
84
|
docker build -t ${{ env.REGISTRY }}/<%= config.appName %>-api:${{ github.sha }} \
|
|
79
85
|
-f apps/api/Dockerfile .
|
|
86
|
+
- name: Validate API runtime image
|
|
87
|
+
run: |
|
|
88
|
+
docker run --rm --entrypoint sh \
|
|
89
|
+
${{ env.REGISTRY }}/<%= config.appName %>-api:${{ github.sha }} \
|
|
90
|
+
-c '
|
|
91
|
+
set -eu
|
|
92
|
+
test -f /app/apps/api/dist/apps/api/src/main.js
|
|
93
|
+
node -e "require(\"@prisma/client\")"
|
|
94
|
+
node -e "require(\"/app/apps/api/dist/packages/api-prisma/src/generated-client.js\")"
|
|
95
|
+
'
|
|
80
96
|
- name: Push Docker image (sha)
|
|
81
97
|
run: |
|
|
82
98
|
docker push ${{ env.REGISTRY }}/<%= config.appName %>-api:${{ github.sha }}
|
|
@@ -159,14 +175,28 @@ jobs:
|
|
|
159
175
|
kubectl get pods -l app=<%= config.appName %>-api -n ${{ env.NAMESPACE }}
|
|
160
176
|
echo "--- Recent events:"
|
|
161
177
|
kubectl describe deployment/<%= config.appName %>-api -n ${{ env.NAMESPACE }} | tail -20
|
|
178
|
+
POD_NAME=$(kubectl get pods -l app=<%= config.appName %>-api -n ${{ env.NAMESPACE }} --sort-by=.metadata.creationTimestamp -o name | tail -n 1 | cut -d/ -f2)
|
|
179
|
+
if [ -n "$POD_NAME" ]; then
|
|
180
|
+
echo "--- Describe newest pod: $POD_NAME"
|
|
181
|
+
kubectl describe pod "$POD_NAME" -n ${{ env.NAMESPACE }}
|
|
182
|
+
echo "--- Current logs from $POD_NAME:"
|
|
183
|
+
kubectl logs "$POD_NAME" -n ${{ env.NAMESPACE }} --tail=120 || true
|
|
184
|
+
echo "--- Previous logs from $POD_NAME:"
|
|
185
|
+
kubectl logs "$POD_NAME" -n ${{ env.NAMESPACE }} --previous --tail=120 || true
|
|
186
|
+
fi
|
|
162
187
|
kubectl logs -l app=<%= config.appName %>-api -n ${{ env.NAMESPACE }} --tail=50 --previous 2>/dev/null || true
|
|
163
188
|
exit 1
|
|
164
189
|
)
|
|
165
|
-
|
|
166
|
-
<%
|
|
190
|
+
<% } %>
|
|
191
|
+
<% if ((config.apps || []).includes('admin')) { %>
|
|
192
|
+
deploy-admin:
|
|
167
193
|
name: Deploy ADMIN
|
|
168
194
|
runs-on: ubuntu-latest
|
|
169
|
-
needs:
|
|
195
|
+
needs:
|
|
196
|
+
- apply-cluster-config
|
|
197
|
+
<% if ((config.apps || []).includes('api')) { %>
|
|
198
|
+
- deploy-api
|
|
199
|
+
<% } %>
|
|
170
200
|
steps:
|
|
171
201
|
- name: Checkout code
|
|
172
202
|
uses: actions/checkout@v4
|
|
@@ -230,69 +260,16 @@ jobs:
|
|
|
230
260
|
kubectl get pods -l app=<%= config.appName %>-admin -n ${{ env.NAMESPACE }}
|
|
231
261
|
echo "--- Recent events:"
|
|
232
262
|
kubectl describe deployment/<%= config.appName %>-admin -n ${{ env.NAMESPACE }} | tail -20
|
|
263
|
+
POD_NAME=$(kubectl get pods -l app=<%= config.appName %>-admin -n ${{ env.NAMESPACE }} --sort-by=.metadata.creationTimestamp -o name | tail -n 1 | cut -d/ -f2)
|
|
264
|
+
if [ -n "$POD_NAME" ]; then
|
|
265
|
+
echo "--- Describe newest pod: $POD_NAME"
|
|
266
|
+
kubectl describe pod "$POD_NAME" -n ${{ env.NAMESPACE }}
|
|
267
|
+
echo "--- Current logs from $POD_NAME:"
|
|
268
|
+
kubectl logs "$POD_NAME" -n ${{ env.NAMESPACE }} --tail=120 || true
|
|
269
|
+
echo "--- Previous logs from $POD_NAME:"
|
|
270
|
+
kubectl logs "$POD_NAME" -n ${{ env.NAMESPACE }} --previous --tail=120 || true
|
|
271
|
+
fi
|
|
233
272
|
kubectl logs -l app=<%= config.appName %>-admin -n ${{ env.NAMESPACE }} --tail=50 --previous 2>/dev/null || true
|
|
234
273
|
exit 1
|
|
235
274
|
)
|
|
236
|
-
<% }
|
|
237
|
-
deploy-<%= app %>:
|
|
238
|
-
name: Deploy <%= app.toUpperCase() %>
|
|
239
|
-
runs-on: ubuntu-latest
|
|
240
|
-
needs: apply-cluster-config
|
|
241
|
-
steps:
|
|
242
|
-
- name: Checkout code
|
|
243
|
-
uses: actions/checkout@v4
|
|
244
|
-
- name: Install doctl
|
|
245
|
-
uses: digitalocean/action-doctl@v2
|
|
246
|
-
with:
|
|
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"
|
|
256
|
-
- name: Log in to Container Registry
|
|
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
|
|
272
|
-
run: |
|
|
273
|
-
docker build -t ${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:${{ github.sha }} \
|
|
274
|
-
-f apps/<%= app %>/Dockerfile .
|
|
275
|
-
- name: Push Docker image (sha)
|
|
276
|
-
run: |
|
|
277
|
-
docker push ${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:${{ github.sha }}
|
|
278
|
-
- name: Push Docker image (latest)
|
|
279
|
-
run: |
|
|
280
|
-
docker tag ${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:${{ github.sha }} \
|
|
281
|
-
${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:latest
|
|
282
|
-
docker push ${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:latest
|
|
283
|
-
- name: Save DigitalOcean kubeconfig
|
|
284
|
-
run: doctl kubernetes cluster kubeconfig save ${{ env.K8S_CLUSTER_ID }}
|
|
285
|
-
- name: Deploy to Kubernetes
|
|
286
|
-
run: |
|
|
287
|
-
kubectl set image deployment/<%= config.appName %>-<%= app %> \
|
|
288
|
-
<%= config.appName %>-<%= app %>=${{ env.REGISTRY }}/<%= config.appName %>-<%= app %>:${{ github.sha }} \
|
|
289
|
-
-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
|
-
)
|
|
298
|
-
<% }); %>
|
|
275
|
+
<% } %>
|