@hed-hog/cli 0.0.67 → 0.0.69
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/commands/dev.command/deploy-config.subcommand.d.ts +3 -1
- package/dist/src/commands/dev.command/deploy-config.subcommand.js +18 -2
- package/dist/src/commands/dev.command/deploy-config.subcommand.js.map +1 -1
- package/dist/src/modules/developer/developer.service.d.ts +21 -1
- package/dist/src/modules/developer/developer.service.js +310 -112
- package/dist/src/modules/developer/developer.service.js.map +1 -1
- package/dist/src/templates/deployment/DEPLOYMENT.md.ejs +20 -4
- package/dist/src/templates/deployment/k8s.deployment.yaml.ejs +15 -0
- package/dist/src/templates/deployment/workflow.deploy.yml.ejs +46 -23
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -42,6 +42,15 @@ kubectl get namespace <%= config.namespace %>
|
|
|
42
42
|
Add the following secrets to your GitHub repository:
|
|
43
43
|
|
|
44
44
|
1. `DIGITALOCEAN_ACCESS_TOKEN` - Your Digital Ocean API token
|
|
45
|
+
2. `DATABASE_URL`, `JWT_SECRET`, `ENCRYPTION_SECRET`, and `PEPPER` - API runtime secrets required by the deploy workflow
|
|
46
|
+
|
|
47
|
+
You can configure the application secrets directly from the CLI by rerunning:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
hedhog dev deploy-config
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The CLI can generate the random values for `JWT_SECRET`, `ENCRYPTION_SECRET`, and `PEPPER`, ask for `DATABASE_URL`, and publish them with `gh secret set`.
|
|
45
54
|
|
|
46
55
|
```bash
|
|
47
56
|
# Get your DO token and add it to GitHub
|
|
@@ -131,14 +140,21 @@ kubectl set image deployment/<%= config.appName %>-api <%= config.appName %>-api
|
|
|
131
140
|
kubectl rollout status deployment/<%= config.appName %>-api -n <%= config.namespace %>
|
|
132
141
|
|
|
133
142
|
<% } %><% 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.
|
|
143
|
+
--build-arg NEXT_PUBLIC_API_BASE_URL=<%= config.apiDomain || '' %> \
|
|
144
|
+
--build-arg NEXT_PUBLIC_API_URL=<%= config.apiDomain || '' %> \
|
|
145
|
+
--build-arg INTERNAL_API_URL=http://<%= config.appName %>-api:<%= config.appPorts?.api || 3000 %> \
|
|
146
|
+
--build-arg ADMIN_API_BASE_URL=<%= config.apiDomain || '' %> \
|
|
147
|
+
--build-arg ADMIN_API_URL=<%= config.apiDomain || '' %> \
|
|
148
|
+
--build-arg ADMIN_INTERNAL_API_URL=http://<%= config.appName %>-api:<%= config.appPorts?.api || 3000 %> \
|
|
136
149
|
-f apps/admin/Dockerfile .
|
|
137
150
|
docker push <%= config.containerRegistry %>/<%= config.appName %>-admin:latest
|
|
138
151
|
kubectl create configmap <%= config.appName %>-admin-config \
|
|
139
152
|
-n <%= config.namespace %> \
|
|
140
|
-
--from-literal=
|
|
141
|
-
--from-literal=
|
|
153
|
+
--from-literal=ADMIN_API_BASE_URL='<%= config.apiDomain || '' %>' \
|
|
154
|
+
--from-literal=ADMIN_API_URL='<%= config.apiDomain || '' %>' \
|
|
155
|
+
--from-literal=ADMIN_INTERNAL_API_URL='http://<%= config.appName %>-api:<%= config.appPorts?.api || 3000 %>' \
|
|
156
|
+
--from-literal=NEXT_PUBLIC_API_BASE_URL='<%= config.apiDomain || '' %>' \
|
|
157
|
+
--from-literal=NEXT_PUBLIC_API_URL='<%= config.apiDomain || '' %>' \
|
|
142
158
|
--from-literal=INTERNAL_API_URL='http://<%= config.appName %>-api:<%= config.appPorts?.api || 3000 %>' \
|
|
143
159
|
--from-literal=FRONTEND_URLS='<%= config.frontendUrls || (config.domain ? `https://${config.domain}` : '') %>' \
|
|
144
160
|
--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
|
|
|
@@ -285,7 +297,7 @@ jobs:
|
|
|
285
297
|
|
|
286
298
|
echo "Inspecting repository ${REPOSITORY} in registry ${REGISTRY_NAME}"
|
|
287
299
|
|
|
288
|
-
TAG_ROWS="$(doctl registry repository list-tags "${
|
|
300
|
+
TAG_ROWS="$(doctl registry repository list-tags "${REGISTRY_NAME}/${REPOSITORY}" --format Tag,ManifestDigest --no-header || true)"
|
|
289
301
|
|
|
290
302
|
if [ -z "${TAG_ROWS}" ]; then
|
|
291
303
|
echo "No tags found for ${REPOSITORY}; skipping cleanup."
|
|
@@ -336,12 +348,11 @@ jobs:
|
|
|
336
348
|
continue
|
|
337
349
|
fi
|
|
338
350
|
|
|
339
|
-
doctl registry repository delete-manifest "${REPOSITORY}" "${DIGEST}" --
|
|
351
|
+
doctl registry repository delete-manifest "${REGISTRY_NAME}/${REPOSITORY}" "${DIGEST}" --force
|
|
340
352
|
done
|
|
341
353
|
}
|
|
342
354
|
|
|
343
355
|
cleanup_repository "<%= config.appName %>-api"
|
|
344
|
-
cleanup_repository "<%= config.appName %>-api-migrate"
|
|
345
356
|
<% } %>
|
|
346
357
|
<% if ((config.apps || []).includes('admin')) { %>
|
|
347
358
|
deploy-admin:
|
|
@@ -354,11 +365,17 @@ jobs:
|
|
|
354
365
|
<% } %>
|
|
355
366
|
steps:
|
|
356
367
|
- name: Checkout code
|
|
357
|
-
uses: actions/checkout@
|
|
368
|
+
uses: actions/checkout@v6
|
|
358
369
|
- name: Install doctl
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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 }}'
|
|
362
379
|
- name: Validate DigitalOcean token
|
|
363
380
|
run: |
|
|
364
381
|
doctl auth validate
|
|
@@ -426,10 +443,13 @@ jobs:
|
|
|
426
443
|
esac
|
|
427
444
|
- name: Build Docker image
|
|
428
445
|
run: |
|
|
429
|
-
docker build -t ${{ env.REGISTRY }}
|
|
446
|
+
docker build -t ${{ env.REGISTRY }}/<%= config.appName %>-admin:${{ github.sha }} \
|
|
430
447
|
--build-arg NEXT_PUBLIC_API_BASE_URL=${{ env.ADMIN_API_BASE_URL }} \
|
|
431
448
|
--build-arg NEXT_PUBLIC_API_URL=${{ env.ADMIN_API_URL }} \
|
|
432
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 }} \
|
|
433
453
|
-f apps/admin/Dockerfile .
|
|
434
454
|
- name: Push Docker image (sha)
|
|
435
455
|
run: |
|
|
@@ -445,6 +465,9 @@ jobs:
|
|
|
445
465
|
run: |
|
|
446
466
|
kubectl create configmap <%= config.appName %>-admin-config \
|
|
447
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 }}' \
|
|
448
471
|
--from-literal=NEXT_PUBLIC_API_BASE_URL='${{ env.ADMIN_API_BASE_URL }}' \
|
|
449
472
|
--from-literal=NEXT_PUBLIC_API_URL='${{ env.ADMIN_API_URL }}' \
|
|
450
473
|
--from-literal=INTERNAL_API_URL='${{ env.ADMIN_INTERNAL_API_URL }}' \
|
|
@@ -481,7 +504,7 @@ jobs:
|
|
|
481
504
|
|
|
482
505
|
echo "Inspecting repository ${REPOSITORY} in registry ${REGISTRY_NAME}"
|
|
483
506
|
|
|
484
|
-
TAG_ROWS="$(doctl registry repository list-tags "${
|
|
507
|
+
TAG_ROWS="$(doctl registry repository list-tags "${REGISTRY_NAME}/${REPOSITORY}" --format Tag,ManifestDigest --no-header || true)"
|
|
485
508
|
|
|
486
509
|
if [ -z "${TAG_ROWS}" ]; then
|
|
487
510
|
echo "No tags found for ${REPOSITORY}; skipping cleanup."
|
|
@@ -532,6 +555,6 @@ jobs:
|
|
|
532
555
|
continue
|
|
533
556
|
fi
|
|
534
557
|
|
|
535
|
-
doctl registry repository delete-manifest "${REPOSITORY}" "${DIGEST}" --
|
|
558
|
+
doctl registry repository delete-manifest "${REGISTRY_NAME}/${REPOSITORY}" "${DIGEST}" --force
|
|
536
559
|
done
|
|
537
560
|
<% } %>
|