@hed-hog/cli 0.0.66 → 0.0.68
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/templates/deployment/DEPLOYMENT.md.ejs +11 -4
- package/dist/src/templates/deployment/k8s.deployment.yaml.ejs +15 -0
- package/dist/src/templates/deployment/workflow.deploy.yml.ejs +96 -27
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/package.json
CHANGED
|
@@ -131,14 +131,21 @@ kubectl set image deployment/<%= config.appName %>-api <%= config.appName %>-api
|
|
|
131
131
|
kubectl rollout status deployment/<%= config.appName %>-api -n <%= config.namespace %>
|
|
132
132
|
|
|
133
133
|
<% } %><% if (config.apps.includes('admin')) { %>docker build -t <%= config.containerRegistry %>/<%= config.appName %>-admin:latest \
|
|
134
|
-
--build-arg NEXT_PUBLIC_API_BASE_URL=<%= config.
|
|
135
|
-
--build-arg NEXT_PUBLIC_API_URL=<%= config.
|
|
134
|
+
--build-arg NEXT_PUBLIC_API_BASE_URL=<%= config.apiDomain || '' %> \
|
|
135
|
+
--build-arg NEXT_PUBLIC_API_URL=<%= config.apiDomain || '' %> \
|
|
136
|
+
--build-arg INTERNAL_API_URL=http://<%= config.appName %>-api:<%= config.appPorts?.api || 3000 %> \
|
|
137
|
+
--build-arg ADMIN_API_BASE_URL=<%= config.apiDomain || '' %> \
|
|
138
|
+
--build-arg ADMIN_API_URL=<%= config.apiDomain || '' %> \
|
|
139
|
+
--build-arg ADMIN_INTERNAL_API_URL=http://<%= config.appName %>-api:<%= config.appPorts?.api || 3000 %> \
|
|
136
140
|
-f apps/admin/Dockerfile .
|
|
137
141
|
docker push <%= config.containerRegistry %>/<%= config.appName %>-admin:latest
|
|
138
142
|
kubectl create configmap <%= config.appName %>-admin-config \
|
|
139
143
|
-n <%= config.namespace %> \
|
|
140
|
-
--from-literal=
|
|
141
|
-
--from-literal=
|
|
144
|
+
--from-literal=ADMIN_API_BASE_URL='<%= config.apiDomain || '' %>' \
|
|
145
|
+
--from-literal=ADMIN_API_URL='<%= config.apiDomain || '' %>' \
|
|
146
|
+
--from-literal=ADMIN_INTERNAL_API_URL='http://<%= config.appName %>-api:<%= config.appPorts?.api || 3000 %>' \
|
|
147
|
+
--from-literal=NEXT_PUBLIC_API_BASE_URL='<%= config.apiDomain || '' %>' \
|
|
148
|
+
--from-literal=NEXT_PUBLIC_API_URL='<%= config.apiDomain || '' %>' \
|
|
142
149
|
--from-literal=INTERNAL_API_URL='http://<%= config.appName %>-api:<%= config.appPorts?.api || 3000 %>' \
|
|
143
150
|
--from-literal=FRONTEND_URLS='<%= config.frontendUrls || (config.domain ? `https://${config.domain}` : '') %>' \
|
|
144
151
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
@@ -24,6 +24,21 @@ spec:
|
|
|
24
24
|
- name: NODE_ENV
|
|
25
25
|
value: production
|
|
26
26
|
<% if (app === 'admin') { %>
|
|
27
|
+
- name: ADMIN_INTERNAL_API_URL
|
|
28
|
+
valueFrom:
|
|
29
|
+
configMapKeyRef:
|
|
30
|
+
name: <%= config.appName %>-<%= app %>-config
|
|
31
|
+
key: ADMIN_INTERNAL_API_URL
|
|
32
|
+
- name: ADMIN_API_BASE_URL
|
|
33
|
+
valueFrom:
|
|
34
|
+
configMapKeyRef:
|
|
35
|
+
name: <%= config.appName %>-<%= app %>-config
|
|
36
|
+
key: ADMIN_API_BASE_URL
|
|
37
|
+
- name: ADMIN_API_URL
|
|
38
|
+
valueFrom:
|
|
39
|
+
configMapKeyRef:
|
|
40
|
+
name: <%= config.appName %>-<%= app %>-config
|
|
41
|
+
key: ADMIN_API_URL
|
|
27
42
|
- name: INTERNAL_API_URL
|
|
28
43
|
valueFrom:
|
|
29
44
|
configMapKeyRef:
|
|
@@ -7,18 +7,18 @@ on:
|
|
|
7
7
|
env:
|
|
8
8
|
REGISTRY: <%= config.containerRegistry %>
|
|
9
9
|
NAMESPACE: <%= config.namespace %>
|
|
10
|
-
K8S_CLUSTER_ID: <%= config.clusterName %>
|
|
10
|
+
K8S_CLUSTER_ID: <%= config.k8sClusterId || config.clusterName %>
|
|
11
11
|
<% if ((config.apps || []).includes('api')) { %>
|
|
12
12
|
# API environment
|
|
13
13
|
API_FRONTEND_URL: <%= config.domain ? 'https://' + config.domain : '' %>
|
|
14
14
|
API_JWT_EXPIRES_IN: <%= config.jwtExpiresIn || '7d' %>
|
|
15
15
|
<% } %>
|
|
16
16
|
<% if ((config.apps || []).includes('admin')) { %>
|
|
17
|
-
# Admin environment (
|
|
17
|
+
# Admin environment (used in image build args and mirrored to runtime ConfigMap)
|
|
18
18
|
ADMIN_API_BASE_URL: <%= config.apiDomain || '' %>
|
|
19
19
|
ADMIN_API_URL: <%= config.apiDomain || '' %>
|
|
20
|
-
ADMIN_INTERNAL_API_URL: http://<%= config.appName %>-api
|
|
21
|
-
FRONTEND_URLS: <%= config.frontendUrls %>
|
|
20
|
+
ADMIN_INTERNAL_API_URL: http://<%= config.appName %>-api:<%= config.appPorts?.api || 3000 %>
|
|
21
|
+
FRONTEND_URLS: <%= config.frontendUrls || (config.domain ? 'https://' + config.domain : '') %>
|
|
22
22
|
<% } %>
|
|
23
23
|
jobs:
|
|
24
24
|
apply-cluster-config:
|
|
@@ -26,11 +26,17 @@ jobs:
|
|
|
26
26
|
runs-on: ubuntu-latest
|
|
27
27
|
steps:
|
|
28
28
|
- name: Checkout code
|
|
29
|
-
uses: actions/checkout@
|
|
29
|
+
uses: actions/checkout@v6
|
|
30
30
|
- name: Install doctl
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
run: |
|
|
32
|
+
set -eu
|
|
33
|
+
DOCTL_VERSION="1.119.1"
|
|
34
|
+
curl -fsSL "https://github.com/digitalocean/doctl/releases/download/v${DOCTL_VERSION}/doctl-${DOCTL_VERSION}-linux-amd64.tar.gz" -o /tmp/doctl.tar.gz
|
|
35
|
+
tar -xzf /tmp/doctl.tar.gz -C /tmp
|
|
36
|
+
sudo mv /tmp/doctl /usr/local/bin/doctl
|
|
37
|
+
doctl version
|
|
38
|
+
- name: Authenticate doctl
|
|
39
|
+
run: doctl auth init -t '${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}'
|
|
34
40
|
- name: Save DigitalOcean kubeconfig
|
|
35
41
|
run: doctl kubernetes cluster kubeconfig save ${{ env.K8S_CLUSTER_ID }}
|
|
36
42
|
- name: Ensure namespace exists
|
|
@@ -61,11 +67,17 @@ jobs:
|
|
|
61
67
|
# - FRONTEND_URL: Frontend URL (currently: ${{ env.API_FRONTEND_URL }})
|
|
62
68
|
steps:
|
|
63
69
|
- name: Checkout code
|
|
64
|
-
uses: actions/checkout@
|
|
70
|
+
uses: actions/checkout@v6
|
|
65
71
|
- name: Install doctl
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
run: |
|
|
73
|
+
set -eu
|
|
74
|
+
DOCTL_VERSION="1.119.1"
|
|
75
|
+
curl -fsSL "https://github.com/digitalocean/doctl/releases/download/v${DOCTL_VERSION}/doctl-${DOCTL_VERSION}-linux-amd64.tar.gz" -o /tmp/doctl.tar.gz
|
|
76
|
+
tar -xzf /tmp/doctl.tar.gz -C /tmp
|
|
77
|
+
sudo mv /tmp/doctl /usr/local/bin/doctl
|
|
78
|
+
doctl version
|
|
79
|
+
- name: Authenticate doctl
|
|
80
|
+
run: doctl auth init -t '${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}'
|
|
69
81
|
- name: Validate DigitalOcean token
|
|
70
82
|
run: |
|
|
71
83
|
doctl auth validate
|
|
@@ -131,7 +143,7 @@ jobs:
|
|
|
131
143
|
# Check DATABASE_URL
|
|
132
144
|
if [ -z "$DATABASE_URL" ]; then
|
|
133
145
|
echo "::error::GitHub secret DATABASE_URL is not set. Configure it in Settings > Secrets and variables > Actions."
|
|
134
|
-
|
|
146
|
+
echo "::error::Expected format: postgresql://postgres:<password>@postgresql-<%= config.appName %>:5432/<%= config.appName %>"
|
|
135
147
|
MISSING_SECRETS="$MISSING_SECRETS DATABASE_URL"
|
|
136
148
|
fi
|
|
137
149
|
|
|
@@ -278,27 +290,45 @@ jobs:
|
|
|
278
290
|
run: |
|
|
279
291
|
set -eu
|
|
280
292
|
REGISTRY_NAME="${REGISTRY##*/}"
|
|
293
|
+
DIGEST_REGEX='^sha256:[a-f0-9]{64}$'
|
|
281
294
|
|
|
282
295
|
cleanup_repository() {
|
|
283
296
|
REPOSITORY="$1"
|
|
284
297
|
|
|
285
298
|
echo "Inspecting repository ${REPOSITORY} in registry ${REGISTRY_NAME}"
|
|
286
299
|
|
|
287
|
-
TAG_ROWS="$(doctl registry repository list-tags "${
|
|
300
|
+
TAG_ROWS="$(doctl registry repository list-tags "${REGISTRY_NAME}/${REPOSITORY}" --format Tag,ManifestDigest --no-header || true)"
|
|
288
301
|
|
|
289
302
|
if [ -z "${TAG_ROWS}" ]; then
|
|
290
303
|
echo "No tags found for ${REPOSITORY}; skipping cleanup."
|
|
291
304
|
return 0
|
|
292
305
|
fi
|
|
293
306
|
|
|
294
|
-
CURRENT_DIGEST="$(printf '%s\n' "${TAG_ROWS}" | awk -v target="${{ github.sha }}"
|
|
307
|
+
CURRENT_DIGEST="$(printf '%s\n' "${TAG_ROWS}" | awk -v target="${{ github.sha }}" -v regex="${DIGEST_REGEX}" '
|
|
308
|
+
$1 == target {
|
|
309
|
+
for (i = 1; i <= NF; i++) {
|
|
310
|
+
if ($i ~ regex) {
|
|
311
|
+
print $i;
|
|
312
|
+
exit;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
')"
|
|
295
317
|
|
|
296
318
|
if [ -z "${CURRENT_DIGEST}" ]; then
|
|
297
319
|
echo "::error::Unable to determine current manifest digest for ${REPOSITORY}:${{ github.sha }}"
|
|
298
320
|
exit 1
|
|
299
321
|
fi
|
|
300
322
|
|
|
301
|
-
OLD_DIGESTS="$(printf '%s\n' "${TAG_ROWS}" | awk -v keep="${CURRENT_DIGEST}"
|
|
323
|
+
OLD_DIGESTS="$(printf '%s\n' "${TAG_ROWS}" | awk -v keep="${CURRENT_DIGEST}" -v regex="${DIGEST_REGEX}" '
|
|
324
|
+
{
|
|
325
|
+
for (i = 1; i <= NF; i++) {
|
|
326
|
+
if ($i ~ regex && $i != keep) {
|
|
327
|
+
print $i;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
' | sort -u)"
|
|
302
332
|
|
|
303
333
|
if [ -z "${OLD_DIGESTS}" ]; then
|
|
304
334
|
echo "No old manifests to delete for ${REPOSITORY}."
|
|
@@ -312,13 +342,17 @@ jobs:
|
|
|
312
342
|
if [ -z "${DIGEST}" ]; then
|
|
313
343
|
continue
|
|
314
344
|
fi
|
|
345
|
+
|
|
346
|
+
if ! printf '%s\n' "${DIGEST}" | grep -Eq "${DIGEST_REGEX}"; then
|
|
347
|
+
echo "Skipping invalid digest candidate: ${DIGEST}"
|
|
348
|
+
continue
|
|
349
|
+
fi
|
|
315
350
|
|
|
316
|
-
doctl registry repository delete-manifest "${REPOSITORY}" "${DIGEST}" --
|
|
351
|
+
doctl registry repository delete-manifest "${REGISTRY_NAME}/${REPOSITORY}" "${DIGEST}" --force
|
|
317
352
|
done
|
|
318
353
|
}
|
|
319
354
|
|
|
320
355
|
cleanup_repository "<%= config.appName %>-api"
|
|
321
|
-
cleanup_repository "<%= config.appName %>-api-migrate"
|
|
322
356
|
<% } %>
|
|
323
357
|
<% if ((config.apps || []).includes('admin')) { %>
|
|
324
358
|
deploy-admin:
|
|
@@ -331,11 +365,17 @@ jobs:
|
|
|
331
365
|
<% } %>
|
|
332
366
|
steps:
|
|
333
367
|
- name: Checkout code
|
|
334
|
-
uses: actions/checkout@
|
|
368
|
+
uses: actions/checkout@v6
|
|
335
369
|
- name: Install doctl
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
370
|
+
run: |
|
|
371
|
+
set -eu
|
|
372
|
+
DOCTL_VERSION="1.119.1"
|
|
373
|
+
curl -fsSL "https://github.com/digitalocean/doctl/releases/download/v${DOCTL_VERSION}/doctl-${DOCTL_VERSION}-linux-amd64.tar.gz" -o /tmp/doctl.tar.gz
|
|
374
|
+
tar -xzf /tmp/doctl.tar.gz -C /tmp
|
|
375
|
+
sudo mv /tmp/doctl /usr/local/bin/doctl
|
|
376
|
+
doctl version
|
|
377
|
+
- name: Authenticate doctl
|
|
378
|
+
run: doctl auth init -t '${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}'
|
|
339
379
|
- name: Validate DigitalOcean token
|
|
340
380
|
run: |
|
|
341
381
|
doctl auth validate
|
|
@@ -403,10 +443,13 @@ jobs:
|
|
|
403
443
|
esac
|
|
404
444
|
- name: Build Docker image
|
|
405
445
|
run: |
|
|
406
|
-
docker build -t ${{ env.REGISTRY }}
|
|
446
|
+
docker build -t ${{ env.REGISTRY }}/<%= config.appName %>-admin:${{ github.sha }} \
|
|
407
447
|
--build-arg NEXT_PUBLIC_API_BASE_URL=${{ env.ADMIN_API_BASE_URL }} \
|
|
408
448
|
--build-arg NEXT_PUBLIC_API_URL=${{ env.ADMIN_API_URL }} \
|
|
409
449
|
--build-arg INTERNAL_API_URL=${{ env.ADMIN_INTERNAL_API_URL }} \
|
|
450
|
+
--build-arg ADMIN_API_BASE_URL=${{ env.ADMIN_API_BASE_URL }} \
|
|
451
|
+
--build-arg ADMIN_API_URL=${{ env.ADMIN_API_URL }} \
|
|
452
|
+
--build-arg ADMIN_INTERNAL_API_URL=${{ env.ADMIN_INTERNAL_API_URL }} \
|
|
410
453
|
-f apps/admin/Dockerfile .
|
|
411
454
|
- name: Push Docker image (sha)
|
|
412
455
|
run: |
|
|
@@ -422,6 +465,9 @@ jobs:
|
|
|
422
465
|
run: |
|
|
423
466
|
kubectl create configmap <%= config.appName %>-admin-config \
|
|
424
467
|
-n ${{ env.NAMESPACE }} \
|
|
468
|
+
--from-literal=ADMIN_API_BASE_URL='${{ env.ADMIN_API_BASE_URL }}' \
|
|
469
|
+
--from-literal=ADMIN_API_URL='${{ env.ADMIN_API_URL }}' \
|
|
470
|
+
--from-literal=ADMIN_INTERNAL_API_URL='${{ env.ADMIN_INTERNAL_API_URL }}' \
|
|
425
471
|
--from-literal=NEXT_PUBLIC_API_BASE_URL='${{ env.ADMIN_API_BASE_URL }}' \
|
|
426
472
|
--from-literal=NEXT_PUBLIC_API_URL='${{ env.ADMIN_API_URL }}' \
|
|
427
473
|
--from-literal=INTERNAL_API_URL='${{ env.ADMIN_INTERNAL_API_URL }}' \
|
|
@@ -454,24 +500,42 @@ jobs:
|
|
|
454
500
|
set -eu
|
|
455
501
|
REGISTRY_NAME="${REGISTRY##*/}"
|
|
456
502
|
REPOSITORY="<%= config.appName %>-admin"
|
|
503
|
+
DIGEST_REGEX='^sha256:[a-f0-9]{64}$'
|
|
457
504
|
|
|
458
505
|
echo "Inspecting repository ${REPOSITORY} in registry ${REGISTRY_NAME}"
|
|
459
506
|
|
|
460
|
-
TAG_ROWS="$(doctl registry repository list-tags "${
|
|
507
|
+
TAG_ROWS="$(doctl registry repository list-tags "${REGISTRY_NAME}/${REPOSITORY}" --format Tag,ManifestDigest --no-header || true)"
|
|
461
508
|
|
|
462
509
|
if [ -z "${TAG_ROWS}" ]; then
|
|
463
510
|
echo "No tags found for ${REPOSITORY}; skipping cleanup."
|
|
464
511
|
exit 0
|
|
465
512
|
fi
|
|
466
513
|
|
|
467
|
-
CURRENT_DIGEST="$(printf '%s\n' "${TAG_ROWS}" | awk -v target="${{ github.sha }}"
|
|
514
|
+
CURRENT_DIGEST="$(printf '%s\n' "${TAG_ROWS}" | awk -v target="${{ github.sha }}" -v regex="${DIGEST_REGEX}" '
|
|
515
|
+
$1 == target {
|
|
516
|
+
for (i = 1; i <= NF; i++) {
|
|
517
|
+
if ($i ~ regex) {
|
|
518
|
+
print $i;
|
|
519
|
+
exit;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
')"
|
|
468
524
|
|
|
469
525
|
if [ -z "${CURRENT_DIGEST}" ]; then
|
|
470
526
|
echo "::error::Unable to determine current manifest digest for ${REPOSITORY}:${{ github.sha }}"
|
|
471
527
|
exit 1
|
|
472
528
|
fi
|
|
473
529
|
|
|
474
|
-
OLD_DIGESTS="$(printf '%s\n' "${TAG_ROWS}" | awk -v keep="${CURRENT_DIGEST}"
|
|
530
|
+
OLD_DIGESTS="$(printf '%s\n' "${TAG_ROWS}" | awk -v keep="${CURRENT_DIGEST}" -v regex="${DIGEST_REGEX}" '
|
|
531
|
+
{
|
|
532
|
+
for (i = 1; i <= NF; i++) {
|
|
533
|
+
if ($i ~ regex && $i != keep) {
|
|
534
|
+
print $i;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
' | sort -u)"
|
|
475
539
|
|
|
476
540
|
if [ -z "${OLD_DIGESTS}" ]; then
|
|
477
541
|
echo "No old manifests to delete for ${REPOSITORY}."
|
|
@@ -485,7 +549,12 @@ jobs:
|
|
|
485
549
|
if [ -z "${DIGEST}" ]; then
|
|
486
550
|
continue
|
|
487
551
|
fi
|
|
552
|
+
|
|
553
|
+
if ! printf '%s\n' "${DIGEST}" | grep -Eq "${DIGEST_REGEX}"; then
|
|
554
|
+
echo "Skipping invalid digest candidate: ${DIGEST}"
|
|
555
|
+
continue
|
|
556
|
+
fi
|
|
488
557
|
|
|
489
|
-
doctl registry repository delete-manifest "${REPOSITORY}" "${DIGEST}" --
|
|
558
|
+
doctl registry repository delete-manifest "${REGISTRY_NAME}/${REPOSITORY}" "${DIGEST}" --force
|
|
490
559
|
done
|
|
491
560
|
<% } %>
|