underpost 3.2.70 → 3.2.90

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 (60) hide show
  1. package/.github/workflows/publish.ci.yml +3 -3
  2. package/.github/workflows/release.cd.yml +1 -1
  3. package/CHANGELOG.md +1358 -1038
  4. package/CLI-HELP.md +39 -16
  5. package/README.md +3 -3
  6. package/bin/build.js +10 -4
  7. package/bin/deploy.js +18 -16
  8. package/docker-compose.yml +1 -1
  9. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  10. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  11. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  12. package/manifests/deployment/playwright/deployment.yaml +1 -1
  13. package/manifests/mongodb/kustomization.yaml +4 -1
  14. package/manifests/mongodb/statefulset.yaml +4 -0
  15. package/manifests/mongodb/storage-class.yaml +9 -2
  16. package/package.json +20 -20
  17. package/scripts/nat-iptables.sh +10 -4
  18. package/scripts/test-monitor.sh +4 -3
  19. package/src/api/core/core.controller.js +4 -65
  20. package/src/api/core/core.router.js +8 -14
  21. package/src/api/default/default.controller.js +2 -70
  22. package/src/api/default/default.router.js +7 -17
  23. package/src/api/document/document.controller.js +5 -77
  24. package/src/api/document/document.router.js +9 -13
  25. package/src/api/file/file.controller.js +9 -53
  26. package/src/api/file/file.router.js +14 -6
  27. package/src/api/test/test.controller.js +8 -53
  28. package/src/api/test/test.router.js +1 -4
  29. package/src/cli/cluster.js +771 -66
  30. package/src/cli/db.js +6 -4
  31. package/src/cli/deploy.js +1715 -168
  32. package/src/cli/docker-compose.js +19 -24
  33. package/src/cli/fs.js +0 -1
  34. package/src/cli/image.js +40 -13
  35. package/src/cli/index.js +129 -35
  36. package/src/cli/ipfs.js +82 -11
  37. package/src/cli/monitor.js +1 -1
  38. package/src/cli/release.js +4 -0
  39. package/src/cli/repository.js +14 -3
  40. package/src/cli/run.js +2253 -439
  41. package/src/cli/secrets.js +969 -0
  42. package/src/cli/ssh.js +38 -39
  43. package/src/client/components/core/Modal.js +38 -4
  44. package/src/client-builder/client-build.js +94 -11
  45. package/src/client-builder/ssr.js +27 -73
  46. package/src/db/mongo/MongoBootstrap.js +295 -54
  47. package/src/db/mongo/MongooseDB.js +47 -32
  48. package/src/index.js +1 -1
  49. package/src/server/conf.js +1307 -6
  50. package/src/server/cri.js +70 -0
  51. package/src/server/downloader.js +3 -3
  52. package/src/server/middlewares.js +152 -0
  53. package/src/server/underpost-gateway.js +1073 -0
  54. package/src/server/underpost-ingress.js +364 -0
  55. package/test/cluster-instances.test.js +435 -0
  56. package/test/deploy-node-placement.test.js +45 -0
  57. package/test/instance-traffic-plan.test.js +710 -0
  58. package/test/sops-secret-store.test.js +612 -0
  59. package/test/underpost-gateway.test.js +469 -0
  60. package/test/underpost-ingress.test.js +253 -0
package/CLI-HELP.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ## Underpost CLI
2
2
 
3
- > underpost ci/cd cli v3.2.70
3
+ > underpost ci/cd cli v3.2.90
4
4
 
5
5
  **Usage:** `underpost [options] [command]`
6
6
 
@@ -210,6 +210,7 @@ Manages commits to a GitHub repository, supporting various commit types and opti
210
210
  | `--edit` | Edit last commit. |
211
211
  | `--deploy-id <deploy-id>` | Sets the deployment configuration ID for the commit context. |
212
212
  | `--cached` | Commit staged changes only or context. |
213
+ | `--init-repo [origin]` | Initialize a git repository at the specified path. Optionally set the git remote origin URL. |
213
214
  | `--hashes <hashes>` | Comma-separated list of specific file hashes of commits. |
214
215
  | `--extension <extension>` | specific file extensions of commits. |
215
216
  | `--changelog` | Print the plain changelog of the last N commits (see --from-n-commit, default 1). |
@@ -410,7 +411,7 @@ Manages Kubernetes clusters, defaulting to Kind cluster initialization.
410
411
  | Option | Description |
411
412
  | --- | --- |
412
413
  | `--reset` | Deletes all clusters and prunes all related data and caches. |
413
- | `--reset-mongodb` | Performs a hard cleanup of only MongoDB-related resources (StatefulSet, PVCs/PVs, Secrets, ConfigMaps, caches) without restarting the whole node. |
414
+ | `--reset-mongodb` | Performs a hard cleanup of only MongoDB-related resources (StatefulSet, PVCs/PVs, Secrets, ConfigMaps, caches) without restarting the whole node. Combined with --mongodb it instead wipes the retained hostPath volumes as part of that deploy, so the replica set starts from empty data. |
414
415
  | `--mariadb` | Initializes the cluster with a MariaDB statefulset. |
415
416
  | `--mysql` | Initializes the cluster with a MySQL statefulset. |
416
417
  | `--mongodb` | Initializes the cluster with a MongoDB statefulset. |
@@ -420,6 +421,9 @@ Manages Kubernetes clusters, defaulting to Kind cluster initialization.
420
421
  | `--valkey` | Initializes the cluster with a Valkey service. |
421
422
  | `--ipfs` | Initializes the cluster with an ipfs-cluster statefulset. |
422
423
  | `--contour` | Initializes the cluster with Project Contour base HTTPProxy and Envoy. |
424
+ | `--gateway-api` | Initializes the cluster with the Gateway API control plane (CRDs, Envoy Gateway, GatewayClass) used by generated HTTPRoute + QUIC/HTTP3 manifests. With --dev the data plane binds the listener ports on the host network for direct browser access. |
425
+ | `--gateway-class <name>` | GatewayClass name to provision (default "eg"). |
426
+ | `--ingress-node <node-name>` | Dedicated node for underpost-ingress when both routing stacks coexist. Workload placement flags do not move it. |
423
427
  | `--node-port` | Exposes enabled ready services (e.g. MongoDB 4.4, Valkey) to the host/public network via their NodePort Service manifest. |
424
428
  | `--node-selector <k8s-node-name>` | Pins the just-deployed StatefulSet (MongoDB 4.4 / Valkey) to the given Kubernetes node once it is ready (via a kubernetes.io/hostname nodeSelector). |
425
429
  | `--cert-manager` | Initializes the cluster with a Let's Encrypt production ClusterIssuer. |
@@ -467,13 +471,14 @@ Manages application deployments, defaulting to deploying development pods.
467
471
  | `--remove` | Deletes specified deployments and their associated services. |
468
472
  | `--sync` | Synchronizes deployment environment variables, ports, and replica counts. |
469
473
  | `--info-router` | Displays the current router structure and configuration. |
470
- | `--expose` | Exposes services matching the provided deployment ID list. |
471
474
  | `--cert` | Resets TLS/SSL certificate secrets for deployments. |
472
475
  | `--cert-hosts <hosts>` | Resets TLS/SSL certificate secrets for specified hosts. |
473
476
  | `--self-signed` | Use a pre-created self-signed TLS secret (kubernetes.io/tls) instead of cert-manager. The secret must already exist in the namespace with the same name as the host. Enables TLS in the Contour HTTPProxy virtualhost without requiring a production ClusterIssuer. |
474
477
  | `--node <node>` | Sets optional node for deployment operations. |
478
+ | `--ingress-node <node-name>` | Explicitly relocates the shared host-network ingress; ordinary --node placement never moves it. |
475
479
  | `--ssh-key-path <path>` | Private key path for node SSH operations. Currently used when shipping a hostPath volume to a remote target node over SSH. Defaults to engine-private/deploy/id_rsa. |
476
480
  | `--build-manifest` | Builds Kubernetes YAML manifests, including deployments, services, proxies, and secrets. |
481
+ | `--sync-static` | Places the SSR status pages and intercepted contexts in the gateway static utility tree, so the edge serves them instead of the application pods. Prefers the running workload and falls back to this checkout, so it can seed the tree before the deployment exists and refresh it once the deployment is Ready. |
477
482
  | `--replicas <replicas>` | Sets a custom number of replicas for deployments. |
478
483
  | `--image <image>` | Sets a custom image for deployments. |
479
484
  | `--versions <deployment-versions>` | A comma-separated list of custom deployment versions. |
@@ -483,28 +488,25 @@ Manages application deployments, defaulting to deploying development pods.
483
488
  | `--retry-count <count>` | Sets HTTPProxy per-route retry count (e.g., 3). |
484
489
  | `--retry-per-try-timeout <duration>` | Sets HTTPProxy retry per-try timeout (e.g., "150ms"). |
485
490
  | `--disable-update-deployment` | Disables updates to deployments. |
486
- | `--disable-runtime-probes` | Omits the internal-status HTTP probes from generated deployment manifests. |
491
+ | `--disable-runtime-probes` | Deprecated compatibility flag; readiness probes remain mandatory. Use --tcp-probes for legacy workloads. |
487
492
  | `--tcp-probes` | Generates legacy TCP socket probes instead of HTTP internal-status probes (migration). |
488
493
  | `--disable-update-proxy` | Disables updates to proxies. |
489
494
  | `--disable-deployment-proxy` | Disables proxies of deployments. |
495
+ | `--gateway-api` | Routes through the Gateway API stack (Gateway + HTTPRoute) instead of the Contour HTTPProxy. Both manifest sets are always generated; this selects which one is applied. |
496
+ | `--gateway-class <name>` | GatewayClass name for generated Gateway manifests (default "eg"). |
497
+ | `--disable-http3` | Omits the QUIC/HTTP3 listener config and the Alt-Svc advertisement from Gateway API manifests. |
498
+ | `--quic-port <port>` | UDP port advertised for QUIC/HTTP3 in generated Gateway API manifests (default 443). |
490
499
  | `--disable-update-volume` | Disables updates to volume mounts during deployment. |
491
- | `--status` | Retrieves current network traffic data from resource deployments and the host machine network configuration. |
492
500
  | `--kubeadm` | Enables the kubeadm context for deployment operations. |
493
501
  | `--k3s` | Enables the k3s context for deployment operations. |
494
502
  | `--kind` | Enables the kind context for deployment operations. |
495
503
  | `--git-clean` | Runs git clean on volume mount paths before copying. |
496
504
  | `--disable-update-underpost-config` | Disables updates to Underpost configuration during deployment. |
497
505
  | `--namespace <namespace>` | Kubernetes namespace for deployment operations (defaults to "default"). |
498
- | `--kind-type <kind-type>` | Specifies the Kind cluster type for deployment operations. |
499
- | `--port <port>` | Sets up port forwarding from local to remote ports. |
500
- | `--expose-port <port>` | Sets the local:remote port to expose when --expose is active (overrides auto-detected service port). |
501
- | `--expose-local-port <port>` | Sets a different local port for --expose (e.g. 80) while keeping the remote service port. Useful for /etc/hosts local access without specifying a port in the browser. |
502
- | `--local-proxy` | Forward all service TCP ports locally and start the Node.js path-routing proxy. Enables full path-based routing (e.g. /wp alongside /) without needing --expose-local-port. Requires --expose. |
503
506
  | `--cmd <cmd>` | Custom initialization command for deployment (comma-separated commands). |
504
507
  | `--skip-full-build` | Skip client bundle rebuild; container will pull pre-built bundle via pull-bundle instead. |
505
508
  | `--pull-bundle` | Explicitly pull the pre-built client bundle from Cloudinary inside the container. Use together with --skip-full-build. |
506
509
  | `--image-pull-policy <policy>` | Override container imagePullPolicy in the generated deployment manifest (Always, IfNotPresent, Never). Defaults to Never for localhost/ images and IfNotPresent otherwise. |
507
- | `--tls` | Enables TLS for the local proxy started by --expose --local-proxy. The proxy will serve HTTPS on port 443 using self-signed certificates resolved from the local SSL store. Use together with --expose and --local-proxy. |
508
510
  | `-h, --help` | display help for command |
509
511
 
510
512
  ---
@@ -513,13 +515,13 @@ Manages application deployments, defaulting to deploying development pods.
513
515
 
514
516
  Manages secrets for various platforms.
515
517
 
516
- **Usage:** `underpost secret [options] <platform>`
518
+ **Usage:** `underpost secret [options] [platform]`
517
519
 
518
520
  #### Arguments
519
521
 
520
522
  | Argument | Description |
521
523
  | --- | --- |
522
- | `platform` | The secret management platform. Options: underpost, sanitizeSecretEnvFile, globalSecretClean. |
524
+ | `platform` | The secret management platform. Options: underpost, sops, sanitizeSecretEnvFile, globalSecretClean. Defaults to "sops". (default: "sops") |
523
525
 
524
526
  #### Options
525
527
 
@@ -530,6 +532,17 @@ Manages secrets for various platforms.
530
532
  | `--create-from-env` | Creates secrets from container environment variables (envFrom: secretRef). |
531
533
  | `--global-clean` | Removes all filesystem traces of secrets (engine-private, .env, conf cache). |
532
534
  | `--list` | Lists all available secrets for the platform. |
535
+ | `--encrypt <plaintext-path>` | Encrypts a plaintext Secret manifest into the Git-tracked SOPS store and shreds the source (sops platform). |
536
+ | `--apply` | Decrypts stored SOPS manifests and streams them into kubectl apply, without writing plaintext to disk (sops platform). |
537
+ | `--namespace <namespace>` | Kubernetes namespace for secret operations (defaults to "default"). |
538
+ | `--install-tools` | Installs the sops and age host binaries only, without running a full cluster host initialization. |
539
+ | `--rotate` | Re-keys every stored SOPS manifest onto --recipient. Secret values are unchanged, so no workload restart is needed. |
540
+ | `--recipient <age-public-key>` | Incoming Age public recipient for --rotate. |
541
+ | `--prune-recipients` | With --rotate, makes --recipient the only recipient, revoking every previous key (use after a key compromise). Requires --force, and revokes CI/CD keys too unless they are named in --keep-recipients. |
542
+ | `--keep-recipients <age-public-keys>` | Comma-separated recipients to retain while --prune-recipients revokes the rest (e.g. the CI/CD key). |
543
+ | `--purge <secret-name>` | Emergency removal: deletes the live Kubernetes Secret and takes its encrypted manifest out of the store. |
544
+ | `--force` | Confirms the irreversible variant: deletes the manifest instead of archiving it (--purge), revokes recipients (--rotate --prune-recipients), or replaces an existing manifest (--encrypt). |
545
+ | `--dry-run` | Reports what --apply, --rotate, or --purge would do without changing anything. |
533
546
  | `-h, --help` | display help for command |
534
547
 
535
548
  ---
@@ -824,7 +837,7 @@ Runs specified scripts using various runners.
824
837
 
825
838
  | Argument | Description |
826
839
  | --- | --- |
827
- | `runner-id` | The runner ID to run. Options: dev-cluster,etc-hosts,ipfs-expose,metadata,svc-ls,svc-rm,ssh-deploy-info,node-move,dev-hosts-expose,dev-hosts-restore,cluster-build,template-deploy,template-deploy-local,docker-image,clean,pull,release-deploy,ssh-deploy,ide,crypto-policy,sync,stop,ssh-deploy-stop,ssh-deploy-db-rollback,ssh-deploy-db,ssh-deploy-db-status,tz,get-proxy,instance-promote,instance,deploy-key,instance-build-manifest,ls-deployments,host-update,install-crio,dd-container,ip-info,db-client,git-conf,promote,metrics,cluster,deploy,disk-clean,disk-devices,disk-usage,dev,service,sh,log,ps,pid-info,background,ports,deploy-test,tf-vae-test,spark-template,pull-rocky-image,rmi,kill,generate-pass,secret,underpost-config,gpu-env,tf-gpu-test,deploy-job,push-bundle,pull-bundle,build-cluster-deployment-manifests,monitor-ui,shared-dir,shared-dir-add-user. |
840
+ | `runner-id` | The runner ID to run. Options: status,expose,dev-cluster,metadata,ipfs-expose,svc-ls,svc-rm,node-move,dev-hosts-expose,dev-hosts-restore,cluster-build,template-deploy,template-deploy-local,docker-image,clean,pull,release-deploy,ssh-deploy,ide,crypto-policy,sync,stop,tz,get-traffic,restore-mongo,ingress-refresh,instance-promote,instance,deploy-key,instance-build-manifest,ls-deployments,host-update,install-crio,dd-container,ip-info,db-client,git-conf,promote,metrics,cluster,gateway-status,deploy,disk-clean,disk-devices,disk-usage,dev,service,etc-hosts,sh,log,ps,pid-info,background,ports,deploy-test,tf-vae-test,spark-template,pull-rocky-image,rmi,kill,generate-pass,sops-setup,sops-status,secret,underpost-config,gpu-env,tf-gpu-test,deploy-job,push-bundle,pull-bundle,build-cluster-deployment-manifests,monitor-ui,shared-dir,shared-dir-add-user. |
828
841
  | `path` | The input value, identifier, or path for the operation. |
829
842
 
830
843
  #### Options
@@ -838,8 +851,12 @@ Runs specified scripts using various runners.
838
851
  | `--replicas <replicas>` | Sets a custom number of replicas for deployment. |
839
852
  | `--pod-name <pod-name>` | Optional: Specifies the pod name for execution. |
840
853
  | `--node-name <node-name>` | Optional: Specifies the node name for execution. |
854
+ | `--ingress-node <node-name>` | Dedicated node for the host-network underpost-ingress listener. Workload --node-name never relocates it. |
841
855
  | `--ssh-key-path <path>` | Optional: Private key path for node SSH operations, forwarded to volume shipping over SSH. Defaults to engine-private/deploy/id_rsa. |
842
856
  | `--port <port>` | Optional: Specifies the port for execution. |
857
+ | `--expose-container-ports <ports>` | Comma-separated Service/container ports; multiple matched resources consume values by resource index. |
858
+ | `--expose-host-ports <ports>` | Comma-separated host ports paired with container ports by resource/port index. |
859
+ | `--local-proxy` | Starts the development path proxy after the expose runner creates its port-forwards. |
843
860
  | `--etc-hosts` | Enables etc-hosts context for the runner execution. |
844
861
  | `--volume-host-path <volume-host-path>` | Optional: Specifies the volume host path for test execution. |
845
862
  | `--volume-mount-path <volume-mount-path>` | Optional: Specifies the volume mount path for test execution. |
@@ -866,7 +883,7 @@ Runs specified scripts using various runners.
866
883
  | `--limits-memory <limits-memory>` | Sets memory limit for the runner execution. |
867
884
  | `--limits-cpu <limits-cpu>` | Sets CPU limit for the runner execution. |
868
885
  | `--resource-template-id <resource-template-id >` | Specifies a resource template ID for the runner execution. |
869
- | `--expose` | Enables service exposure for the runner execution. |
886
+ | `--expose` | Enables exposure-only behavior in compatible runners; the expose runner itself does not require this flag. |
870
887
  | `--conf-server-path <conf-server-path>` | Sets a custom configuration server path. |
871
888
  | `--underpost-root <underpost-root>` | Sets a custom Underpost root path. |
872
889
  | `--cmd-cron-jobs <cmd-cron-jobs>` | Pre-script commands to run before cron job execution. |
@@ -875,7 +892,7 @@ Runs specified scripts using various runners.
875
892
  | `--kubeadm` | Sets the kubeadm cluster context for the runner execution. |
876
893
  | `--k3s` | Sets the k3s cluster context for the runner execution. |
877
894
  | `--kind` | Sets the kind cluster context for the runner execution. |
878
- | `--traffic <traffic>` | Blue/green traffic colour to bake into generated manifests (default: blue). |
895
+ | `--traffic <traffic>` | Blue/green traffic colour to bake into generated manifests (default: blue). `stop` accepts a comma list, e.g. blue,green. |
879
896
  | `--git-clean` | Runs git clean on volume mount paths before copying. |
880
897
  | `--deploy-id <deploy-id>` | Sets deploy id context for the runner execution. |
881
898
  | `--user <user>` | Sets user context for the runner execution. |
@@ -886,6 +903,11 @@ Runs specified scripts using various runners.
886
903
  | `--timeout-idle <duration>` | Sets HTTPProxy per-route idle timeout (e.g., "10s", "infinity"). |
887
904
  | `--retry-count <count>` | Sets HTTPProxy per-route retry count (e.g., 3). |
888
905
  | `--retry-per-try-timeout <duration>` | Sets HTTPProxy retry per-try timeout (e.g., "150ms"). |
906
+ | `--gateway-api` | Routes through the Gateway API stack (Gateway + HTTPRoute) instead of the Contour HTTPProxy. Both manifest sets are always generated; this selects which one is applied. |
907
+ | `--disable-gateway-api` | Falls back to the Contour HTTPProxy stack in runners where the Gateway API is the default (cluster). |
908
+ | `--gateway-class <name>` | GatewayClass name for generated Gateway manifests (default "eg"). |
909
+ | `--disable-http3` | Omits the QUIC/HTTP3 listener config and the Alt-Svc advertisement from Gateway API manifests. |
910
+ | `--quic-port <port>` | UDP port advertised for QUIC/HTTP3 in generated Gateway API manifests (default 443). |
889
911
  | `--disable-private-conf-update` | Disables updates to private configuration during execution. |
890
912
  | `--logs` | Streams logs during the runner execution. |
891
913
  | `--monitor-status <status>` | Sets the status to monitor for pod/resource (default: "Running"). |
@@ -901,6 +923,7 @@ Runs specified scripts using various runners.
901
923
  | `--pull-bundle` | Explicitly download the pre-built client bundle from Cloudinary inside the container (supported by: sync, template-deploy). Use together with --skip-full-build. |
902
924
  | `--remove` | Remove/teardown resources |
903
925
  | `--test` | Enables test/generic-purpose mode for the runner (e.g. use self-signed TLS instead of cert-manager). |
926
+ | `--branch <branch>` | Sets the branch for git operations (default: current branch). |
904
927
  | `-h, --help` | display help for command |
905
928
 
906
929
  ---
package/README.md CHANGED
@@ -10,13 +10,13 @@
10
10
 
11
11
  <div align="center">
12
12
 
13
- <a target="_top" href='https://download.rockylinux.org/pub/rocky/9/'><img alt='rockylinux' src='https://img.shields.io/badge/Rocky Linux v9.6-100000?style=flat&logo=rockylinux&logoColor=white&labelColor=10b981&color=727273'/></a> <a target="_top" href='https://www.npmjs.com/package/npm?activeTab=versions'><img alt='npm' src='https://img.shields.io/badge/npm v11.6.2-100000?style=flat&logo=npm&logoColor=white&labelColor=CB3837&color=727273'/></a> <a target="_top" href='https://nodejs.org/download/release'><img alt='nodedotjs' src='https://img.shields.io/badge/node v24.15.0-100000?style=flat&logo=nodedotjs&logoColor=white&labelColor=5FA04E&color=727273'/></a> <a target="_top" href='https://pgp.mongodb.com/'><img alt='mongodb' src='https://img.shields.io/badge/mongodb_server v7.0-100000?style=flat&logo=mongodb&logoColor=white&labelColor=47A248&color=727273'/></a>
13
+ <a target="_top" href='https://download.rockylinux.org/pub/rocky/9/'><img alt='rockylinux' src='https://img.shields.io/badge/Rocky Linux v9.8-100000?style=flat&logo=rockylinux&logoColor=white&labelColor=10b981&color=727273'/></a> <a target="_top" href='https://www.npmjs.com/package/npm?activeTab=versions'><img alt='npm' src='https://img.shields.io/badge/npm v11.6.2-100000?style=flat&logo=npm&logoColor=white&labelColor=CB3837&color=727273'/></a> <a target="_top" href='https://nodejs.org/download/release'><img alt='nodedotjs' src='https://img.shields.io/badge/node v24.15.0-100000?style=flat&logo=nodedotjs&logoColor=white&labelColor=5FA04E&color=727273'/></a> <a target="_top" href='https://pgp.mongodb.com/'><img alt='mongodb' src='https://img.shields.io/badge/mongodb_server v7.0-100000?style=flat&logo=mongodb&logoColor=white&labelColor=47A248&color=727273'/></a>
14
14
 
15
15
  </div>
16
16
 
17
17
  <div align="center">
18
18
 
19
- [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![](https://data.jsdelivr.com/v1/package/npm/underpost/badge)](https://www.jsdelivr.com/package/npm/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/3.2.70)](https://socket.dev/npm/package/underpost/overview/3.2.70) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
19
+ [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![](https://data.jsdelivr.com/v1/package/npm/underpost/badge)](https://www.jsdelivr.com/package/npm/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/3.2.90)](https://socket.dev/npm/package/underpost/overview/3.2.90) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
20
20
 
21
21
  </div>
22
22
 
@@ -88,7 +88,7 @@ npm run dev
88
88
  <!-- cli-index-start -->
89
89
  ## Underpost CLI
90
90
 
91
- > underpost ci/cd cli v3.2.70
91
+ > underpost ci/cd cli v3.2.90
92
92
 
93
93
  **Usage:** `underpost [options] [command]`
94
94
 
package/bin/build.js CHANGED
@@ -14,7 +14,8 @@ import {
14
14
  updatePrivateEngineTestRepo,
15
15
  } from '../src/server/conf.js';
16
16
  import { loadDeployCatalog } from '../src/server/catalog.js';
17
- import UnderpostRepository from '../src/cli/repository.js';
17
+ import Underpost from '../src/index.js';
18
+ import { DOCKER_SCRIPTS } from '../src/api/cyberia-server-defaults/cyberia-server-defaults.js';
18
19
 
19
20
  const baseConfPath = './engine-private/conf/dd-cron/.env.production';
20
21
  if (fs.existsSync(baseConfPath)) dotenv.config({ path: baseConfPath, override: true });
@@ -34,8 +35,8 @@ const buildDeployTemplate = async (confName) => {
34
35
  const catalog = await loadDeployCatalog(confName);
35
36
 
36
37
  if (catalog.sourceMoves.length) {
37
- UnderpostRepository.API.sparseCheckoutDirectory(`conf/${confName}`);
38
- if (catalog.sourceMoves.some(([src]) => !fs.existsSync(src))) UnderpostRepository.API.pullSourceRepo(repoName);
38
+ Underpost.repo.sparseCheckoutDirectory(`conf/${confName}`);
39
+ if (catalog.sourceMoves.some(([src]) => !fs.existsSync(src))) Underpost.repo.pullSourceRepo(repoName);
39
40
  }
40
41
  syncDeployIdSources(catalog.sourceMoves);
41
42
 
@@ -125,9 +126,14 @@ const buildDeployTemplate = async (confName) => {
125
126
  packageJson.description = catalog.description;
126
127
  const { CyberiaDependencies } = await import(`../src/api/cyberia-server-defaults/cyberia-server-defaults.js`);
127
128
  packageJson.dependencies = {
128
- ...originPackageJson.dependencies,
129
+ underpost: '^' + Underpost.version.replace('v', ''),
130
+ 'adm-zip': '^0.6.0',
129
131
  ...CyberiaDependencies,
130
132
  };
133
+ packageJson.scripts = {
134
+ ...packageJson.scripts,
135
+ ...DOCKER_SCRIPTS,
136
+ };
131
137
  fs.writeFileSync(`${basePath}/bin/index.js`, fs.readFileSync(`./bin/cyberia.js`, 'utf8'), 'utf8');
132
138
  // Canonical Cyberia doc; engine-cyberia/README.md is a generated copy — never hand-edited.
133
139
  fs.writeFileSync(
package/bin/deploy.js CHANGED
@@ -223,21 +223,23 @@ try {
223
223
  .readFileSync(`./engine-private/deploy/dd.router`, 'utf8')
224
224
  .split(`,`)
225
225
  .concat(['dd-cron'])) {
226
- if (fs.existsSync(`./engine-private/conf/${deployId}/build/development/deployment.yaml`))
227
- fs.copySync(
228
- `./engine-private/conf/${deployId}/build/development/deployment.yaml`,
229
- `./manifests/deployment/${deployId}-development/deployment.yaml`,
230
- );
231
- if (fs.existsSync(`./engine-private/conf/${deployId}/build/development/proxy.yaml`))
232
- fs.copySync(
233
- `./engine-private/conf/${deployId}/build/development/proxy.yaml`,
234
- `./manifests/deployment/${deployId}-development/proxy.yaml`,
235
- );
236
- if (fs.existsSync(`./engine-private/conf/${deployId}/build/development/pv-pvc.yaml`))
237
- fs.copySync(
238
- `./engine-private/conf/${deployId}/build/development/pv-pvc.yaml`,
239
- `./manifests/deployment/${deployId}-development/pv-pvc.yaml`,
240
- );
226
+ // The same set `deploy --build-manifest` mirrors. Anything missing here
227
+ // is a manifest the project repo silently stops carrying while the
228
+ // private build dir still has it.
229
+ for (const file of [
230
+ 'deployment.yaml',
231
+ 'proxy.yaml',
232
+ 'gateway.yaml',
233
+ 'httproute.yaml',
234
+ 'pv-pvc.yaml',
235
+ 'traffic-service.yaml',
236
+ 'grpc-service.yaml',
237
+ ]) {
238
+ const source = `./engine-private/conf/${deployId}/build/development/${file}`;
239
+ const target = `./manifests/deployment/${deployId}-development/${file}`;
240
+ if (fs.existsSync(source)) fs.copySync(source, target);
241
+ else fs.removeSync(target);
242
+ }
241
243
  shellExec(`node bin new --dev --default-conf --deploy-id ${deployId}`);
242
244
  }
243
245
  break;
@@ -763,7 +765,7 @@ nvidia/gpu-operator \
763
765
 
764
766
  case 'pw-conf': {
765
767
  const scriptPath = process.argv[3];
766
- shellExec(`kubectl delete configmap playwright-script`);
768
+ shellExec(`kubectl delete configmap playwright-script --ignore-not-found`);
767
769
  shellExec(`kubectl create configmap playwright-script \
768
770
  --from-file=script.js=${scriptPath} \
769
771
  --dry-run=client -o yaml | kubectl apply -f -
@@ -81,7 +81,7 @@ services:
81
81
  # (dd-default-development-blue). Connection targets use Docker service
82
82
  # discovery instead of hardcoded localhost.
83
83
  app:
84
- image: ${APP_IMAGE:-underpost/underpost-engine}:${APP_TAG:-v3.2.70}
84
+ image: ${APP_IMAGE:-underpost/underpost-engine}:${APP_TAG:-v3.2.90}
85
85
  container_name: dd-app
86
86
  # The start command is supplied by the generated override docker/compose.app.yml
87
87
  # (see `underpost docker-compose --generate --deploy-id <id>`), keeping this
@@ -23,7 +23,7 @@ spec:
23
23
  spec:
24
24
  containers:
25
25
  - name: dd-cron-backup
26
- image: underpost/underpost-engine:v3.2.70
26
+ image: underpost/underpost-engine:v3.2.90
27
27
  command:
28
28
  - /bin/sh
29
29
  - -c
@@ -23,7 +23,7 @@ spec:
23
23
  spec:
24
24
  containers:
25
25
  - name: dd-cron-dns
26
- image: underpost/underpost-engine:v3.2.70
26
+ image: underpost/underpost-engine:v3.2.90
27
27
  command:
28
28
  - /bin/sh
29
29
  - -c
@@ -17,7 +17,7 @@ spec:
17
17
  spec:
18
18
  containers:
19
19
  - name: dd-default-development-blue
20
- image: underpost/underpost-engine:v3.2.70
20
+ image: underpost/underpost-engine:v3.2.90
21
21
  # resources:
22
22
  # requests:
23
23
  # memory: "124Ki"
@@ -98,7 +98,7 @@ spec:
98
98
  spec:
99
99
  containers:
100
100
  - name: dd-default-development-green
101
- image: underpost/underpost-engine:v3.2.70
101
+ image: underpost/underpost-engine:v3.2.90
102
102
  # resources:
103
103
  # requests:
104
104
  # memory: "124Ki"
@@ -14,7 +14,7 @@ spec:
14
14
  spec:
15
15
  containers:
16
16
  - name: playwright
17
- image: mcr.microsoft.com/playwright:v1.59.0-jammy
17
+ image: mcr.microsoft.com/playwright:v1.62.1-jammy
18
18
  command:
19
19
  - /bin/bash
20
20
  - -c
@@ -3,7 +3,10 @@
3
3
  apiVersion: kustomize.config.k8s.io/v1beta1
4
4
  kind: Kustomization
5
5
  resources:
6
- - pv-pvc.yaml
6
+ # pv-pvc.yaml is intentionally absent: the PersistentVolume set is one-per-replica, so it is
7
+ # generated from the effective replica count by MongoBootstrap.applyReplicaVolumes() and applied
8
+ # just before this kustomization. A static file can only describe a fixed number of members, and
9
+ # any --replicas above it leaves the surplus claims unbindable.
7
10
  - headless-service.yaml
8
11
  - statefulset.yaml
9
12
  # - backup-pv-pvc.yaml
@@ -105,6 +105,10 @@ spec:
105
105
  volumeClaimTemplates:
106
106
  - metadata:
107
107
  name: mongodb-storage
108
+ # Without this label `kubectl delete pvc -l app=mongodb` matches nothing, leaving stale
109
+ # claims bound to retained PVs across redeploys.
110
+ labels:
111
+ app: mongodb
108
112
  spec:
109
113
  accessModes: ['ReadWriteOnce']
110
114
  storageClassName: mongodb-storage-class
@@ -3,7 +3,14 @@ kind: StorageClass
3
3
  metadata:
4
4
  name: mongodb-storage-class
5
5
  annotations:
6
- storageclass.kubernetes.io/is-default-class: "false"
7
- provisioner: rancher.io/local-path
6
+ storageclass.kubernetes.io/is-default-class: 'false'
7
+ # Static class: the replica volumes are hostPath PVs generated one per member by
8
+ # MongoBootstrap.applyReplicaVolumes(), each pinned to one member via `claimRef`. It must NOT name a dynamic provisioner — with
9
+ # `rancher.io/local-path` the local-path controller also provisions for these PVCs, racing the
10
+ # static binder, so which volume a member ends up on is not deterministic. Two members landing
11
+ # on one directory is fatal: mongod dies on `WiredTiger.lock: fcntl: Resource temporarily
12
+ # unavailable`, since kind bind-mounts /data/mongodb from the host into every node.
13
+ provisioner: kubernetes.io/no-provisioner
8
14
  reclaimPolicy: Retain
15
+ # Static hostPath PVs carry no nodeAffinity, so binding can safely wait for the scheduler.
9
16
  volumeBindingMode: WaitForFirstConsumer
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "module",
3
3
  "main": "src/index.js",
4
4
  "name": "underpost",
5
- "version": "3.2.70",
5
+ "version": "3.2.90",
6
6
  "description": "Underpost Platform — end-to-end CI/CD and application-delivery toolchain CLI. Covers bare metal, Kubernetes, K3s, kubeadm, LXD, container/image orchestration, secrets, databases, cron jobs, monitoring, SSH, runners, PWA + Workbox delivery, and release orchestration. Extensible via downstream CLIs.",
7
7
  "scripts": {
8
8
  "start": "node --max-old-space-size=8192 src/server",
@@ -71,17 +71,17 @@
71
71
  },
72
72
  "homepage": "https://github.com/underpostnet/pwa-microservices-template#readme",
73
73
  "dependencies": {
74
- "@fortawesome/fontawesome-free": "^7.3.0",
75
- "@fullcalendar/rrule": "^6.1.21",
74
+ "@fortawesome/fontawesome-free": "^7.3.1",
75
+ "@fullcalendar/rrule": "^7.0.2",
76
76
  "@grpc/grpc-js": "^1.14.4",
77
77
  "@grpc/proto-loader": "^0.8.1",
78
78
  "@neodrag/vanilla": "^2.3.1",
79
- "adm-zip": "^0.5.18",
80
- "ag-grid-community": "^36.0.0",
81
- "axios": "^1.18.1",
82
- "bumpp": "^11.1.0",
79
+ "adm-zip": "^0.6.0",
80
+ "ag-grid-community": "^36.1.0",
81
+ "bumpp": "^12.2.0",
82
+ "axios": "^1.19.0",
83
83
  "chai": "^6.2.2",
84
- "clipboardy": "^5.3.1",
84
+ "clipboardy": "^5.3.2",
85
85
  "cloudinary": "^2.10.0",
86
86
  "colors": "^1.4.0",
87
87
  "commander": "^15.0.0",
@@ -96,25 +96,25 @@
96
96
  "escape-string-regexp": "^5.0.0",
97
97
  "express": "^5.2.1",
98
98
  "express-fileupload": "^1.4.3",
99
- "express-rate-limit": "^8.5.2",
99
+ "express-rate-limit": "^8.6.2",
100
100
  "express-slow-down": "^3.1.0",
101
101
  "fast-json-stable-stringify": "^2.1.0",
102
- "favicons": "^7.3.0",
103
- "fs-extra": "^11.3.6",
104
- "fullcalendar": "^6.1.21",
105
- "helmet": "^8.2.0",
102
+ "favicons": "^7.3.1",
103
+ "fs-extra": "^11.4.0",
104
+ "fullcalendar": "^7.0.2",
105
+ "helmet": "^8.3.0",
106
106
  "html-minifier-terser": "^7.2.0",
107
107
  "http-proxy-middleware": "^4.2.0",
108
108
  "ignore-walk": "^9.0.0",
109
- "iovalkey": "^0.3.3",
109
+ "iovalkey": "^0.4.0",
110
110
  "json-colorizer": "^3.0.1",
111
111
  "jsonwebtoken": "^9.0.3",
112
112
  "mariadb": "^3.5.3",
113
- "marked": "^18.0.5",
114
- "mocha": "^11.7.6",
115
- "mongoose": "^9.7.3",
113
+ "marked": "^18.0.9",
114
+ "mongoose": "^9.9.1",
115
+ "mocha": "^11.8.0",
116
116
  "morgan": "^1.11.0",
117
- "nodemailer": "^9.0.3",
117
+ "nodemailer": "^9.0.5",
118
118
  "nodemon": "^3.0.1",
119
119
  "peer": "^1.0.2",
120
120
  "peerjs": "^1.5.5",
@@ -129,9 +129,9 @@
129
129
  "swagger-autogen": "^2.23.7",
130
130
  "swagger-ui-express": "^5.0.0",
131
131
  "typedoc": "^0.28.20",
132
- "typescript": "^6.0.3",
132
+ "typescript": "6.0.3",
133
133
  "validator": "^13.15.35",
134
- "vanilla-jsoneditor": "^3.12.0",
134
+ "vanilla-jsoneditor": "^3.13.0",
135
135
  "winston": "^3.19.0",
136
136
  "workbox-background-sync": "^7.4.1",
137
137
  "workbox-cacheable-response": "^7.4.1",
@@ -32,6 +32,12 @@ sudo firewall-cmd --permanent --zone=public --add-service=ssh
32
32
  sudo firewall-cmd --permanent --zone=public --add-service=http
33
33
  sudo firewall-cmd --permanent --zone=public --add-service=https
34
34
 
35
+ # QUIC / HTTP3. The `https` service above covers TCP 443 only, so without this
36
+ # an HTTP/3 gateway advertises `Alt-Svc: h3=":443"` and every client that acts
37
+ # on it silently fails over UDP while HTTP/2 keeps working.
38
+ sudo firewall-cmd --permanent --zone=public --add-port=443/udp
39
+ sudo firewall-cmd --permanent --zone=public --add-port=80/udp
40
+
35
41
  echo "[INFO] Opening Kubernetes control plane ports..."
36
42
 
37
43
  # Kubernetes API Server
@@ -98,10 +104,10 @@ sudo firewall-cmd --reload
98
104
  echo
99
105
  echo "[INFO] Sysctl status:"
100
106
  sudo sysctl \
101
- net.ipv4.ip_forward \
102
- net.ipv6.conf.all.forwarding \
103
- net.bridge.bridge-nf-call-iptables \
104
- net.bridge.bridge-nf-call-ip6tables
107
+ net.ipv4.ip_forward \
108
+ net.ipv6.conf.all.forwarding \
109
+ net.bridge.bridge-nf-call-iptables \
110
+ net.bridge.bridge-nf-call-ip6tables
105
111
 
106
112
  echo
107
113
  echo "[INFO] Firewall status:"
@@ -24,7 +24,7 @@ MODE=runtime # runtime | instance
24
24
  ENV=development # development | production
25
25
  DEPLOY_ID=dd-test # deploy id (instance mode: parent of conf.instances.json)
26
26
  INSTANCE_IDS= # instance mode: csv of ids (default: all in conf.instances.json)
27
- IMAGE=underpost/wp:v3.2.70 # runtime mode image (instance mode reads image from conf)
27
+ IMAGE=underpost/wp:v3.2.90 # runtime mode image (instance mode reads image from conf)
28
28
  VERSIONS=green # csv of blue/green versions
29
29
  REPLICAS=1 # replicas per deployment
30
30
  NAMESPACE=default # k8s namespace
@@ -202,7 +202,8 @@ run_runtime_mode() {
202
202
  setup_tls_secrets $(hosts_from "$SERVER_CONF")
203
203
  expose_flags="--tls"
204
204
  fi
205
- [ "$DO_EXPOSE" = true ] && node bin deploy --expose --local-proxy "$DEPLOY_ID" "$ENV" $expose_flags
205
+ [ "$DO_EXPOSE" = true ] && node bin run expose "${DEPLOY_ID}-${ENV}-traffic-service" --namespace "$NAMESPACE" \
206
+ --local-proxy $DEV_FLAG $CLUSTER_FLAG $expose_flags
206
207
  }
207
208
 
208
209
  # ─────────────────────────── instance mode ───────────────────────────
@@ -234,7 +235,7 @@ run_instance_mode() {
234
235
  # Plain HTTP: port-forward each instance service port directly.
235
236
  for id in "${ids[@]}"; do
236
237
  [ -n "$id" ] || continue
237
- node bin deploy --expose "${DEPLOY_ID}-${id}" "$ENV" --namespace "$NAMESPACE" || true
238
+ node bin run expose "${DEPLOY_ID}-${id}-${ENV}-traffic-service" --namespace "$NAMESPACE" $CLUSTER_FLAG || true
238
239
  done
239
240
  fi
240
241
  }
@@ -1,69 +1,8 @@
1
- import { loggerFactory } from '../../server/logger.js';
1
+ import { buildCrudController, serviceHandler } from '../../server/middlewares.js';
2
2
  import { CoreService } from './core.service.js';
3
3
 
4
- const logger = loggerFactory(import.meta);
5
-
6
- class CoreController {
7
- static post = async (req, res, options) => {
8
- try {
9
- const result = await CoreService.post(req, res, options);
10
- return res.status(200).json({
11
- status: 'success',
12
- data: result,
13
- });
14
- } catch (error) {
15
- logger.error(error, error.stack);
16
- return res.status(400).json({
17
- status: 'error',
18
- message: error.message,
19
- });
20
- }
21
- };
22
- static get = async (req, res, options) => {
23
- try {
24
- const result = await CoreService.put(req, res, options);
25
- return res.status(200).json({
26
- status: 'success',
27
- data: result,
28
- });
29
- } catch (error) {
30
- logger.error(error, error.stack);
31
- return res.status(400).json({
32
- status: 'error',
33
- message: error.message,
34
- });
35
- }
36
- };
37
- static put = async (req, res, options) => {
38
- try {
39
- const result = await CoreService.get(req, res, options);
40
- return res.status(200).json({
41
- status: 'success',
42
- data: result,
43
- });
44
- } catch (error) {
45
- logger.error(error, error.stack);
46
- return res.status(400).json({
47
- status: 'error',
48
- message: error.message,
49
- });
50
- }
51
- };
52
- static delete = async (req, res, options) => {
53
- try {
54
- const result = await CoreService.delete(req, res, options);
55
- return res.status(200).json({
56
- status: 'success',
57
- data: result,
58
- });
59
- } catch (error) {
60
- logger.error(error, error.stack);
61
- return res.status(400).json({
62
- status: 'error',
63
- message: error.message,
64
- });
65
- }
66
- };
67
- }
4
+ const CoreController = buildCrudController(CoreService, {
5
+ get: serviceHandler(CoreService.get),
6
+ });
68
7
 
69
8
  export { CoreController };