@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/cli",
3
- "version": "0.0.66",
3
+ "version": "0.0.68",
4
4
  "description": "HedHog CLI tool",
5
5
  "author": "HedHog",
6
6
  "private": false,
@@ -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.domain ? `https://api.${config.domain}` : '' %> \
135
- --build-arg NEXT_PUBLIC_API_URL=<%= config.domain ? `https://api.${config.domain}` : '' %> \
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=NEXT_PUBLIC_API_BASE_URL='<%= config.domain ? `https://api.${config.domain}` : '' %>' \
141
- --from-literal=NEXT_PUBLIC_API_URL='<%= config.domain ? `https://api.${config.domain}` : '' %>' \
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 (runtime-replaced at container startup)
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:3100
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@v4
29
+ uses: actions/checkout@v6
30
30
  - name: Install doctl
31
- uses: digitalocean/action-doctl@v2
32
- with:
33
- token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
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@v4
70
+ uses: actions/checkout@v6
65
71
  - name: Install doctl
66
- uses: digitalocean/action-doctl@v2
67
- with:
68
- token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
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
- echo "::error::Expected format: postgresql://postgres:<password>@postgresql-<%= config.appName %>:5432/<%= config.appName %>"
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 "${REPOSITORY}" --registry "${REGISTRY_NAME}" --format Tag,ManifestDigest --no-header || true)"
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 }}" '$1 == target { print $2; exit }')"
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}" 'NF >= 2 && $2 != keep { print $2 }' | sort -u)"
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}" --registry "${REGISTRY_NAME}" --force
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@v4
368
+ uses: actions/checkout@v6
335
369
  - name: Install doctl
336
- uses: digitalocean/action-doctl@v2
337
- with:
338
- token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
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 }}/hub-admin:${{ github.sha }} \
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 "${REPOSITORY}" --registry "${REGISTRY_NAME}" --format Tag,ManifestDigest --no-header || true)"
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 }}" '$1 == target { print $2; exit }')"
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}" 'NF >= 2 && $2 != keep { print $2 }' | sort -u)"
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}" --registry "${REGISTRY_NAME}" --force
558
+ doctl registry repository delete-manifest "${REGISTRY_NAME}/${REPOSITORY}" "${DIGEST}" --force
490
559
  done
491
560
  <% } %>