@hed-hog/cli 0.0.54 → 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.
Files changed (27) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/commands/dev.command/apply.subcommand.d.ts +3 -1
  3. package/dist/src/commands/dev.command/apply.subcommand.js +16 -2
  4. package/dist/src/commands/dev.command/apply.subcommand.js.map +1 -1
  5. package/dist/src/commands/dev.command/assets-to-library.subcommand.d.ts +3 -1
  6. package/dist/src/commands/dev.command/assets-to-library.subcommand.js +29 -3
  7. package/dist/src/commands/dev.command/assets-to-library.subcommand.js.map +1 -1
  8. package/dist/src/commands/dev.command/sync-publish.subcommand.js +11 -1
  9. package/dist/src/commands/dev.command/sync-publish.subcommand.js.map +1 -1
  10. package/dist/src/modules/database/database.service.d.ts +1 -0
  11. package/dist/src/modules/database/database.service.js +30 -10
  12. package/dist/src/modules/database/database.service.js.map +1 -1
  13. package/dist/src/modules/developer/developer.service.d.ts +5 -0
  14. package/dist/src/modules/developer/developer.service.js +186 -21
  15. package/dist/src/modules/developer/developer.service.js.map +1 -1
  16. package/dist/src/modules/hedhog/hedhog.service.d.ts +1 -0
  17. package/dist/src/modules/hedhog/hedhog.service.js +48 -0
  18. package/dist/src/modules/hedhog/hedhog.service.js.map +1 -1
  19. package/dist/src/templates/deployment/DEPLOYMENT.md.ejs +9 -1
  20. package/dist/src/templates/deployment/k8s.configmap.yaml.ejs +11 -0
  21. package/dist/src/templates/deployment/k8s.deployment.yaml.ejs +33 -10
  22. package/dist/src/templates/deployment/k8s.postgres.readme.ms.ejs +172 -0
  23. package/dist/src/templates/deployment/k8s.postgres.service.yaml.ejs +34 -0
  24. package/dist/src/templates/deployment/k8s.postgres.statefulset.yaml.ejs +100 -0
  25. package/dist/src/templates/deployment/workflow.deploy.yml.ejs +190 -25
  26. package/dist/tsconfig.build.tsbuildinfo +1 -1
  27. 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
- API_PUBLIC_URL: <%= config.domain ? `https://api.${config.domain}` : '' %>
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.CLUSTER_NAME }}
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) { %> - name: Apply Helm chart(s)
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: Build and push Docker image
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.CLUSTER_NAME }}
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: Build and push Docker image
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.CLUSTER_NAME }}
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: Build and push Docker image
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.CLUSTER_NAME }}
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
  <% }); %>