@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.
@@ -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 || 'production' %>
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.k8sClusterId || config.clusterName %>
10
+ K8S_CLUSTER_ID: <%= config.clusterName %>
11
+ <% if ((config.apps || []).includes('api')) { %>
11
12
  # API environment
12
- API_FRONTEND_URL: <%= config.frontendUrl || (config.domain ? `https://${config.domain}` : '') %>
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.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}` : '') %>
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
- <% if (config.setupSSL) { %> kubectl apply -f k8s/cert-manager-issuer.yaml
38
- <% } %><% (config.apps || []).forEach((app) => { %> kubectl apply -f k8s/<%= app %> -n ${{ env.NAMESPACE }}
39
- <% }); %><% if (config.setupIngress) { %> kubectl apply -f k8s/ingress.yaml -n ${{ env.NAMESPACE }}
40
- <% } %><% if ((config.infraServices || []).length > 0) { %>
41
+ <% (config.apps || []).forEach(function(app) { %>
42
+ kubectl apply -f k8s/<%= app %> -n ${{ env.NAMESPACE }}
43
+ <% }); %>
44
+
41
45
 
42
- <% if (config.apps.includes('api')) { %> deploy-api:
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
- <% } %><% if (config.apps.includes('admin')) { %> deploy-admin:
190
+ <% } %>
191
+ <% if ((config.apps || []).includes('admin')) { %>
192
+ deploy-admin:
167
193
  name: Deploy ADMIN
168
194
  runs-on: ubuntu-latest
169
- needs: apply-cluster-config
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
- <% } %><% (config.apps || []).filter((app) => app !== 'api' && app !== 'admin').forEach((app) => { %>
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
+ <% } %>