@underpostnet/cyberia 3.2.90 → 3.3.73
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/.dockerignore +1 -0
- package/.env.example +16 -0
- package/.github/workflows/coverall.cyberia.ci.yml +107 -0
- package/.github/workflows/cyberia-client.cd.yml +6 -18
- package/.github/workflows/cyberia-server.cd.yml +6 -18
- package/.github/workflows/docker-image.cyberia-client.ci.yml +8 -5
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +8 -3
- package/.github/workflows/docker-image.cyberia-server.ci.yml +8 -5
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +8 -5
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +15 -1
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +15 -1
- package/.github/workflows/engine-cyberia.cd.yml +6 -78
- package/.github/workflows/engine-cyberia.ci.yml +4 -5
- package/.github/workflows/ghpkg.ci.yml +42 -21
- package/.github/workflows/hardhat.ci.yml +12 -11
- package/.github/workflows/npmpkg.ci.yml +17 -15
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -16
- package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
- package/.github/workflows/release.cd.yml +2 -9
- package/.prettierignore +1 -0
- package/AGENTS.md +47 -0
- package/CHANGELOG.md +581 -2417
- package/CLI-HELP.md +416 -127
- package/Dockerfile +25 -9
- package/Dockerfile.dev +25 -9
- package/Dockerfile.test +4 -4
- package/README.md +21 -1
- package/bin/build.js +70 -43
- package/bin/build.template.js +8 -3
- package/bin/cyberia.js +372 -135
- package/bin/deploy.js +18 -104
- package/bin/index.js +372 -135
- package/compose.env +16 -0
- package/conf.js +93 -4
- package/deploy/cyberia-client/deploy.sh +47 -0
- package/deploy/cyberia-client/package.sh +18 -0
- package/deploy/cyberia-client/state.sh +14 -0
- package/deploy/cyberia-server/deploy.sh +47 -0
- package/deploy/cyberia-server/package.sh +18 -0
- package/deploy/cyberia-server/state.sh +14 -0
- package/deploy/dd-cyberia/deploy.sh +22 -0
- package/deploy/dd-cyberia/init.sh +55 -0
- package/deploy/dd-cyberia/package.sh +17 -0
- package/deploy/dd-cyberia/state.sh +14 -0
- package/deploy/dd-cyberia/sync-deploy.sh +116 -0
- package/deploy/lib/github-actions-logging.sh +594 -0
- package/deploy/lib/host.sh +185 -0
- package/deploy/lib/state.sh +62 -0
- package/deploy/pwa-microservices-template/deploy.sh +45 -0
- package/deploy/release/deploy.sh +32 -0
- package/deployment.yaml +7 -2
- package/docker-compose.yml +31 -37
- package/examples/static-page/README.md +19 -19
- package/examples/static-page/ssr-components/CustomPage.js +2 -2
- package/hardhat/hardhat.config.js +1 -6
- package/hardhat/package-lock.json +104 -43
- package/hardhat/package.json +3 -3
- package/manifests/alertmanager/deployment.yaml +81 -0
- package/manifests/blackbox-exporter/deployment.yaml +75 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +36 -7
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +77 -0
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/grafana/deployment.yaml +26 -7
- package/manifests/grafana/kustomization.yaml +1 -0
- package/manifests/grafana/service.yaml +6 -1
- package/manifests/mariadb/pv.yaml +1 -0
- package/manifests/mongodb/statefulset.yaml +9 -2
- package/manifests/mysql/pv-pvc.yaml +14 -1
- package/manifests/postgresql/pv.yaml +12 -1
- package/manifests/postgresql/pvc.yaml +1 -0
- package/manifests/prometheus/deployment.yaml +18 -22
- package/package.json +83 -10
- package/scripts/audit-selinux.sh +64 -0
- package/scripts/event-monitor.sh +56 -0
- package/scripts/gpu-diag.sh +0 -0
- package/scripts/ip-info.sh +0 -0
- package/scripts/k3s-node-setup.sh +30 -14
- package/scripts/kubeadm-node-setup.sh +40 -31
- package/scripts/lxd-vm-setup.sh +0 -0
- package/scripts/maas-nat-firewalld.sh +0 -0
- package/scripts/maas-setup.sh +8 -8
- package/scripts/nat-iptables.sh +2 -0
- package/scripts/rhel-grpc-setup.sh +0 -0
- package/scripts/rocky-kickstart.sh +25 -9
- package/scripts/selinux-normalize.sh +907 -0
- package/scripts/shutdown-machine.sh +843 -0
- package/scripts/test-monitor.sh +3 -3
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +1 -1
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +2 -2
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +2 -2
- package/src/api/core/core.controller.js +1 -1
- package/src/api/core/core.router.js +2 -2
- package/src/api/core/core.service.js +2 -2
- package/src/api/crypto/crypto.controller.js +1 -1
- package/src/api/crypto/crypto.service.js +1 -1
- package/src/api/cyberia-action/cyberia-action.controller.js +1 -1
- package/src/api/cyberia-action/cyberia-action.router.js +1 -1
- package/src/api/cyberia-action/cyberia-action.service.js +2 -2
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -1
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +1 -1
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +2 -2
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
- package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.service.js +2 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +1 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +1 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +2 -2
- package/src/api/cyberia-instance/cyberia-fallback-capture.js +466 -0
- package/src/api/cyberia-instance/cyberia-fallback-default-items.js +63 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +16 -1
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +1 -1
- package/src/api/cyberia-instance/cyberia-instance.controller.js +4 -1
- package/src/api/cyberia-instance/cyberia-instance.router.js +15 -2
- package/src/api/cyberia-instance/cyberia-instance.service.js +45 -2
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +1 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +2 -2
- package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
- package/src/api/cyberia-map/cyberia-map.router.js +1 -1
- package/src/api/cyberia-map/cyberia-map.service.js +2 -2
- package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
- package/src/api/cyberia-quest/cyberia-quest.router.js +1 -1
- package/src/api/cyberia-quest/cyberia-quest.service.js +2 -2
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
- package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
- package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
- package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +16 -29
- package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
- package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
- package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
- package/src/api/default/default.controller.js +1 -1
- package/src/api/default/default.router.js +1 -1
- package/src/api/default/default.service.js +2 -2
- package/src/api/document/document.controller.js +1 -1
- package/src/api/document/document.router.js +1 -1
- package/src/api/document/document.service.js +2 -2
- package/src/api/file/file.controller.js +1 -1
- package/src/api/file/file.router.js +1 -1
- package/src/api/file/file.service.js +2 -2
- package/src/api/instance/instance.controller.js +1 -1
- package/src/api/instance/instance.router.js +1 -1
- package/src/api/instance/instance.service.js +2 -2
- package/src/api/ipfs/ipfs.controller.js +1 -1
- package/src/api/ipfs/ipfs.router.js +2 -2
- package/src/api/ipfs/ipfs.service.js +2 -2
- package/src/api/object-layer/object-layer.controller.js +1 -1
- package/src/api/object-layer/object-layer.model.js +204 -3
- package/src/api/object-layer/object-layer.router.js +2 -2
- package/src/api/object-layer/object-layer.service.js +4 -17
- package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
- package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
- package/src/api/test/test.controller.js +1 -1
- package/src/api/test/test.service.js +1 -1
- package/src/api/user/guest.service.js +3 -3
- package/src/api/user/user.controller.js +1 -1
- package/src/api/user/user.router.js +2 -2
- package/src/api/user/user.service.js +4 -4
- package/src/api.js +5 -9
- package/src/cli/app.js +391 -0
- package/src/cli/baremetal.js +30 -36
- package/src/cli/client.js +306 -0
- package/src/cli/cloud-init.js +3 -3
- package/src/cli/cluster.js +242 -135
- package/src/cli/db.js +126 -67
- package/src/cli/deploy.js +158 -60
- package/src/cli/docker-compose.js +5 -5
- package/src/cli/domains.js +184 -0
- package/src/cli/dotenv-store.js +143 -0
- package/src/cli/event.js +2281 -0
- package/src/cli/fs.js +3 -3
- package/src/cli/host.js +672 -0
- package/src/cli/image.js +4 -7
- package/src/cli/index.js +426 -134
- package/src/cli/ipfs.js +34 -30
- package/src/cli/kickstart.js +1 -1
- package/src/cli/kubectl.js +218 -41
- package/src/cli/lxd.js +3 -3
- package/src/cli/monitor.js +1359 -26
- package/src/cli/package.js +90 -0
- package/src/cli/release.js +73 -34
- package/src/cli/repository.js +564 -367
- package/src/cli/run.js +362 -654
- package/src/cli/secrets.js +2158 -964
- package/src/cli/ssh.js +651 -331
- package/src/cli/state.js +394 -0
- package/src/cli/static.js +2 -2
- package/src/cli/system.js +26 -13
- package/src/cli/test.js +199 -125
- package/src/cli/vultr.js +623 -0
- package/src/cli/wireguard.js +2930 -0
- package/src/client/components/core/CalendarCore.js +53 -96
- package/src/client/components/core/Docs.js +258 -211
- package/src/client/components/core/FileExplorer.js +0 -21
- package/src/client/components/core/PanelForm.js +4 -4
- package/src/client/components/core/Repository.js +56 -0
- package/src/client/components/core/Translate.js +7 -0
- package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
- package/src/client/components/core/Wallet.js +0 -11
- package/src/client/components/core/Worker.js +7 -1
- package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -2
- package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +368 -0
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +35 -1
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +4 -0
- package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
- package/src/client/components/default/AppShellDefault.js +1 -1
- package/src/client/components/default/SettingsDefault.js +2 -2
- package/src/client/components/itemledger/SettingsItemledger.js +2 -2
- package/src/client/components/underpost/AppShellUnderpost.js +3 -2
- package/src/client/components/underpost/SettingsUnderpost.js +2 -2
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +17 -3
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +32 -5
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +1 -1
- package/src/client/public/cyberia-docs/CYBERIA.md +21 -1
- package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
- package/src/client/public/default/android-chrome-144x144.png +0 -0
- package/src/client/public/default/android-chrome-192x192.png +0 -0
- package/src/client/public/default/android-chrome-256x256.png +0 -0
- package/src/client/public/default/android-chrome-36x36.png +0 -0
- package/src/client/public/default/android-chrome-384x384.png +0 -0
- package/src/client/public/default/android-chrome-48x48.png +0 -0
- package/src/client/public/default/android-chrome-512x512.png +0 -0
- package/src/client/public/default/android-chrome-72x72.png +0 -0
- package/src/client/public/default/android-chrome-96x96.png +0 -0
- package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
- package/src/client/public/default/assets/background/white.jpg +0 -0
- package/src/client/public/default/browserconfig.xml +1 -1
- package/src/client/public/default/favicon-16x16.png +0 -0
- package/src/client/public/default/favicon-32x32.png +0 -0
- package/src/client/public/default/favicon-48x48.png +0 -0
- package/src/client/public/default/favicon.ico +0 -0
- package/src/client/public/default/manifest.webmanifest +5 -5
- package/src/client/public/default/mstile-144x144.png +0 -0
- package/src/client/public/default/mstile-150x150.png +0 -0
- package/src/client/public/default/mstile-310x150.png +0 -0
- package/src/client/public/default/mstile-310x310.png +0 -0
- package/src/client/public/default/mstile-70x70.png +0 -0
- package/src/client/public/default/yandex-browser-50x50.png +0 -0
- package/src/client/public/default/yandex-browser-manifest.json +1 -1
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +40 -0
- package/src/client/services/object-layer/object-layer.management.js +4 -4
- package/src/client/ssr/RootDocument.js +1 -1
- package/src/client/ssr/body/404.js +1 -1
- package/src/client/ssr/body/500.js +1 -1
- package/src/client/ssr/body/CacheControl.js +1 -1
- package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
- package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
- package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
- package/src/client/ssr/head/CryptokoynScripts.js +1 -1
- package/src/client/ssr/head/Css.js +1 -1
- package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
- package/src/client/ssr/head/DefaultScripts.js +1 -1
- package/src/client/ssr/head/ItemledgerScripts.js +1 -1
- package/src/client/ssr/head/Microdata.js +1 -1
- package/src/client/ssr/head/Production.js +1 -1
- package/src/client/ssr/head/Pwa.js +1 -1
- package/src/client/ssr/head/PwaItemledger.js +1 -1
- package/src/client/ssr/head/Seo.js +1 -1
- package/src/client/ssr/head/UnderpostScripts.js +1 -1
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
- package/src/client/ssr/views/Cyberia404.js +7 -5
- package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
- package/src/client/ssr/views/Maintenance.js +1 -1
- package/src/client/ssr/views/NoNetworkConnection.js +1 -1
- package/src/client/ssr/views/Test.js +1 -1
- package/src/client/sw/core.sw.js +20 -14
- package/src/client-builder/client-build-docs.js +61 -46
- package/src/client-builder/client-build-live.js +2 -2
- package/src/client-builder/client-build.js +58 -44
- package/src/client-builder/client-dev-server.js +3 -3
- package/src/client-builder/client-icons.js +2 -2
- package/src/client-builder/ssr.js +3 -3
- package/src/client.build.js +3 -6
- package/src/client.dev.js +3 -6
- package/src/db/DataBaseProvider.js +186 -3
- package/src/db/mariadb/MariaDB.js +3 -2
- package/src/db/mongo/MongoBootstrap.js +273 -101
- package/src/db/mongo/MongooseDB.js +11 -8
- package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
- package/src/grpc/cyberia/grpc-server.js +1 -1
- package/src/index.js +103 -23
- package/src/mailer/EmailRender.js +2 -2
- package/src/mailer/MailerInterceptor.js +87 -0
- package/src/mailer/MailerProvider.js +50 -6
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +1 -1
- package/src/projects/cyberia/besu-genesis-generator.js +3 -3
- package/src/projects/cyberia/catalog-cyberia.js +54 -6
- package/src/projects/cyberia/gemini-client.js +1 -1
- package/src/projects/cyberia/generate-saga.js +15 -24
- package/src/projects/cyberia/hot-reload-trigger.js +1 -1
- package/src/projects/cyberia/instance-data.js +22 -3
- package/src/projects/cyberia/ipfs-client.js +4 -4
- package/src/projects/cyberia/map-preview-generator.js +1 -1
- package/src/projects/cyberia/object-layer.js +12 -22
- package/src/projects/cyberia/semantic-layer-generator.js +1 -1
- package/src/projects/underpost/catalog-underpost.js +13 -2
- package/src/proxy.js +4 -7
- package/src/runtime/cyberia-client/Dockerfile +9 -4
- package/src/runtime/cyberia-client/Dockerfile.dev +8 -2
- package/src/runtime/cyberia-server/Dockerfile +9 -4
- package/src/runtime/cyberia-server/Dockerfile.dev +8 -2
- package/src/runtime/engine-cyberia/Dockerfile +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.test +4 -4
- package/src/runtime/engine-cyberia/compose.env +16 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +31 -37
- package/src/runtime/express/Express.js +32 -27
- package/src/runtime/lampp/Lampp.js +22 -70
- package/src/runtime/nginx/Nginx.js +1 -1
- package/src/runtime/wp/Wp.js +16 -48
- package/src/server/{catalog.js → build/catalog.js} +15 -5
- package/src/server/build/coverage.js +117 -0
- package/src/server/build/execution.js +402 -0
- package/src/server/build/package.js +465 -0
- package/src/server/build/testing.js +528 -0
- package/src/server/{dns.js → network/dns.js} +241 -14
- package/src/server/network/forward-proxy.js +541 -0
- package/src/server/{middlewares.js → network/middlewares.js} +59 -4
- package/src/server/network/node-capability.js +98 -0
- package/src/server/{peer.js → network/peer.js} +3 -3
- package/src/server/{proxy.js → network/proxy.js} +7 -13
- package/src/server/network/router.js +356 -0
- package/src/server/{tls.js → network/tls.js} +2 -2
- package/src/server/network/underpost-compression.js +186 -0
- package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +26 -16
- package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +19 -3
- package/src/server/{cri.js → ops/cri.js} +2 -2
- package/src/server/ops/cron.js +949 -0
- package/src/server/ops/event-notification.js +284 -0
- package/src/server/{logger.js → ops/logger.js} +108 -23
- package/src/server/ops/monitoring.js +1724 -0
- package/src/server/ops/systemd.js +259 -0
- package/src/server/{conf.js → runtime/conf.js} +521 -520
- package/src/server/runtime/config-scope.js +215 -0
- package/src/server/runtime/environment.js +149 -0
- package/src/server/{process.js → runtime/process.js} +153 -13
- package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
- package/src/server/{runtime.js → runtime/runtime.js} +12 -8
- package/src/server/{start.js → runtime/start.js} +128 -39
- package/src/server/{auth.js → security/auth.js} +7 -8
- package/src/server/security/container-storage.js +382 -0
- package/src/server/{crypto.js → security/crypto.js} +1 -1
- package/src/server/security/selinux.js +185 -0
- package/src/server/{backup.js → storage/backup.js} +17 -8
- package/src/server/{data-query.js → storage/data-query.js} +1 -1
- package/src/server/{downloader.js → storage/downloader.js} +2 -2
- package/src/server/storage/repository.js +68 -0
- package/src/server.js +4 -7
- package/src/ws/IoInterface.js +1 -1
- package/src/ws/IoServer.js +1 -1
- package/src/ws/core/core.ws.connection.js +1 -1
- package/src/ws/core/core.ws.emit.js +1 -1
- package/src/ws/core/core.ws.server.js +1 -1
- package/src/ws/default/default.ws.connection.js +1 -1
- package/src/ws/default/default.ws.emit.js +1 -1
- package/src/ws/default/default.ws.server.js +1 -1
- package/test/e2e/event-e2e-public-ingress-down.js +45 -0
- package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
- package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
- package/test/{api.test.js → integration/app/api.test.js} +33 -14
- package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +30 -0
- package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +2 -1
- package/test/integration/app/cyberia/cyberia-load.test.js +467 -0
- package/test/integration/app/cyberia/fallback-world-capture.test.js +253 -0
- package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
- package/test/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
- package/test/integration/infra/1-security/config-scope.test.js +305 -0
- package/test/integration/infra/1-security/container-storage.test.js +356 -0
- package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
- package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
- package/test/integration/infra/1-security/selinux.test.js +69 -0
- package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
- package/test/integration/infra/1-security/systemd-service.test.js +70 -0
- package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
- package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
- package/test/integration/infra/2-network/node-capability.test.js +182 -0
- package/test/integration/infra/2-network/wireguard-cli.test.js +1163 -0
- package/test/integration/infra/2-network/wireguard-edge.test.js +1731 -0
- package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
- package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
- package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
- package/test/integration/infra/4-ingress/deploy-routes.test.js +379 -0
- package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
- package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
- package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +47 -6
- package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +56 -4
- package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
- package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
- package/test/integration/infra/5-observability/event-notification.test.js +197 -0
- package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
- package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
- package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
- package/test/support/shell-harness.js +73 -0
- package/test/unit/build-template.test.js +97 -0
- package/test/unit/build-workflow-offline.test.js +132 -0
- package/test/unit/catalog.test.js +71 -0
- package/test/unit/client-build-docs.test.js +334 -0
- package/test/unit/conf-loading.test.js +303 -0
- package/test/unit/conf-resolution.test.js +979 -0
- package/test/unit/coverage-artifact.test.js +130 -0
- package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
- package/test/unit/deploy-env-resolution.test.js +38 -0
- package/test/unit/deploy-log-table.test.js +447 -0
- package/test/unit/execution-profiles.test.js +219 -0
- package/test/unit/fleet-sync-source.test.js +149 -0
- package/test/unit/logger-redaction.test.js +112 -0
- package/test/unit/package.test.js +403 -0
- package/test/unit/prepare-host.test.js +299 -0
- package/test/unit/process-environment.test.js +18 -0
- package/test/unit/propagation-message.test.js +78 -0
- package/test/unit/redeploy-plan.test.js +24 -0
- package/test/unit/release-bump.test.js +474 -0
- package/test/unit/ssh-output-redaction.test.js +28 -0
- package/test/unit/start-options.test.js +46 -0
- package/test/unit/test-tiers.test.js +193 -0
- package/vitest.config.js +58 -0
- package/.nycrc +0 -9
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
- package/manifests/mongodb/pv-pvc.yaml +0 -59
- package/scripts/link-local-underpost-cli.sh +0 -6
- package/src/api/user/user.build.js +0 -16
- package/src/cli/env.js +0 -177
- package/src/client/components/core/Blockchain.js +0 -41
- package/src/client/components/core/Blog.js +0 -9
- package/src/client/components/core/KeyboardAvoidance.js +0 -145
- package/src/client/public/default/assets/background/dark.jpg +0 -0
- package/src/client/public/default/assets/background/white0-min.jpg +0 -0
- package/src/client/public/default/assets/background/white0.jpg +0 -0
- package/src/client/public/doc/favicon.ico +0 -0
- package/src/client/public/doc/sitemap +0 -148
- package/src/server/cron.js +0 -476
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provides utilities for building, loading, and managing server configurations,
|
|
3
3
|
* deployment contexts, and service configurations (API, Client, WS).
|
|
4
|
-
* @module src/server/conf.js
|
|
4
|
+
* @module src/server/runtime/conf.js
|
|
5
5
|
* @namespace ServerConfBuilder
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -13,20 +13,30 @@ import {
|
|
|
13
13
|
getDirname,
|
|
14
14
|
newInstance,
|
|
15
15
|
orderAbc,
|
|
16
|
-
orderArrayFromAttrInt,
|
|
17
|
-
range,
|
|
18
16
|
timer,
|
|
19
|
-
} from '
|
|
17
|
+
} from '../../client/components/core/CommonJs.js';
|
|
20
18
|
import * as dir from 'path';
|
|
21
19
|
import net from 'net';
|
|
22
20
|
import crypto from 'crypto';
|
|
23
21
|
import colors from 'colors';
|
|
24
|
-
import { loggerFactory } from '
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
22
|
+
import { loggerFactory } from '../ops/logger.js';
|
|
23
|
+
import { isOciRuntime, writeEnv } from './environment.js';
|
|
24
|
+
import { configRejectionFactory } from './config-scope.js';
|
|
25
|
+
import { shellArgumentFactory, shellExec } from './process.js';
|
|
26
|
+
import { UNDERPOST_GATEWAY, statusPageAssetPathFactory } from '../network/underpost-gateway.js';
|
|
27
|
+
import { COVERAGE_BUNDLE_DIRECTORY } from '../build/coverage.js';
|
|
28
|
+
import { DefaultConf } from '../../../conf.js';
|
|
28
29
|
import splitFile from 'split-file';
|
|
29
|
-
import
|
|
30
|
+
import { readDeployRoutes } from '../network/router.js';
|
|
31
|
+
import Underpost from '../../index.js';
|
|
32
|
+
import {
|
|
33
|
+
clearAwaitDeploy,
|
|
34
|
+
getRuntimeStatus,
|
|
35
|
+
isAwaitingDeploy,
|
|
36
|
+
latchAwaitDeploy,
|
|
37
|
+
RUNTIME_STATUS,
|
|
38
|
+
} from './runtime-status.js';
|
|
39
|
+
import { cronDeployIdResolve } from '../ops/cron.js';
|
|
30
40
|
|
|
31
41
|
colors.enable();
|
|
32
42
|
|
|
@@ -45,6 +55,13 @@ const logger = loggerFactory(import.meta);
|
|
|
45
55
|
*/
|
|
46
56
|
const ENV_REF_PREFIX = 'env:';
|
|
47
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Default deploy ID used when no deploy ID is specified.
|
|
60
|
+
* @constant {string}
|
|
61
|
+
* @memberof ServerConfBuilder
|
|
62
|
+
*/
|
|
63
|
+
const DEFAULT_DEPLOY_ID = 'dd-default';
|
|
64
|
+
|
|
48
65
|
/**
|
|
49
66
|
* Resolves a standardized context key from host/path descriptors.
|
|
50
67
|
* The key is used across DB, WS, mailer, and cache registries.
|
|
@@ -166,67 +183,10 @@ const getConfFolder = (deployId) => {
|
|
|
166
183
|
: `./engine-private/conf/${deployId}`;
|
|
167
184
|
};
|
|
168
185
|
|
|
169
|
-
/**
|
|
170
|
-
* Reads `engine-private/deploy/dd.cron` and returns the deploy-id string,
|
|
171
|
-
* or `null` if the file does not exist or is empty.
|
|
172
|
-
*
|
|
173
|
-
* @method cronDeployIdResolve
|
|
174
|
-
* @returns {string|null} The deploy-id from dd.cron, or null.
|
|
175
|
-
* @memberof ServerConfBuilder
|
|
176
|
-
*/
|
|
177
|
-
const cronDeployIdResolve = () => {
|
|
178
|
-
const cronDeployFile = './engine-private/deploy/dd.cron';
|
|
179
|
-
if (fs.existsSync(cronDeployFile)) {
|
|
180
|
-
const id = fs.readFileSync(cronDeployFile, 'utf8').trim();
|
|
181
|
-
return id || null;
|
|
182
|
-
}
|
|
183
|
-
return null;
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Loads the deployment-specific `.env` file referenced by `engine-private/deploy/dd.cron`
|
|
188
|
-
* into `process.env`. Uses `NODE_ENV` to select the environment variant
|
|
189
|
-
* (defaults to `production`).
|
|
190
|
-
*
|
|
191
|
-
* Safe to call multiple times; subsequent calls are no-ops once the env is loaded.
|
|
192
|
-
*
|
|
193
|
-
* @method loadCronDeployEnv
|
|
194
|
-
* @memberof ServerConfBuilder
|
|
195
|
-
*/
|
|
196
|
-
function loadCronDeployEnv() {
|
|
197
|
-
const envName = process.env.NODE_ENV || 'production';
|
|
198
|
-
|
|
199
|
-
// 1) Load dd.cron env (takes full precedence)
|
|
200
|
-
const cronDeployId = cronDeployIdResolve();
|
|
201
|
-
if (cronDeployId) {
|
|
202
|
-
const cronEnvPath = `./engine-private/conf/${cronDeployId}/.env.${envName}`;
|
|
203
|
-
if (fs.existsSync(cronEnvPath)) {
|
|
204
|
-
const cronEnv = dotenv.parse(fs.readFileSync(cronEnvPath, 'utf8'));
|
|
205
|
-
process.env = { ...process.env, ...cronEnv };
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// 2) Load dd.router envs — only keys not already present
|
|
210
|
-
const routerDeployFile = './engine-private/deploy/dd.router';
|
|
211
|
-
if (fs.existsSync(routerDeployFile)) {
|
|
212
|
-
const routerIds = fs.readFileSync(routerDeployFile, 'utf8').trim().split(',');
|
|
213
|
-
for (const deployId of routerIds) {
|
|
214
|
-
const id = deployId.trim();
|
|
215
|
-
if (!id) continue;
|
|
216
|
-
const envPath = `./engine-private/conf/${id}/.env.${envName}`;
|
|
217
|
-
if (!fs.existsSync(envPath)) continue;
|
|
218
|
-
const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
219
|
-
for (const key of Object.keys(env)) {
|
|
220
|
-
if (!(key in process.env)) process.env[key] = env[key];
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
186
|
/**
|
|
227
187
|
* Resolves the full path to a specific configuration JSON file for a deploy ID.
|
|
228
|
-
* For `server` configs
|
|
229
|
-
*
|
|
188
|
+
* For `server` configs it prefers the `dev.<subConf>` variant when one exists: always for a
|
|
189
|
+
* sub-conf the caller named, and only in development for one inherited from `DEPLOY_SUB_CONF`.
|
|
230
190
|
*
|
|
231
191
|
* @method getConfFilePath
|
|
232
192
|
* @param {string} deployId - The deploy ID.
|
|
@@ -240,17 +200,28 @@ function loadCronDeployEnv() {
|
|
|
240
200
|
* // => './engine-private/conf/dd-myapp/conf.server.json'
|
|
241
201
|
*
|
|
242
202
|
* @example
|
|
243
|
-
* //
|
|
203
|
+
* // With an explicitly named subConf, in any environment:
|
|
244
204
|
* getConfFilePath('dd-myapp', 'server', 'local');
|
|
245
205
|
* // => './engine-private/conf/dd-myapp/conf.server.dev.local.json' (if it exists)
|
|
246
206
|
*/
|
|
247
207
|
const getConfFilePath = (deployId, confType, subConf = '') => {
|
|
248
208
|
const folder = getConfFolder(deployId);
|
|
249
|
-
|
|
250
|
-
|
|
209
|
+
const explicit = `${subConf ?? ''}`.trim();
|
|
210
|
+
// Without an explicit sub-conf, fall back to the env var loadConf() exported.
|
|
211
|
+
const effectiveSubConf = explicit || process.env.DEPLOY_SUB_CONF || '';
|
|
251
212
|
if (confType === 'server' && effectiveSubConf) {
|
|
252
|
-
|
|
253
|
-
|
|
213
|
+
// An explicitly named sub-conf is a request, not a hint: honor it whatever the ambient
|
|
214
|
+
// environment says. Only the env-var fallback stays gated on development, so a production
|
|
215
|
+
// deploy that merely carries DEPLOY_SUB_CONF is unaffected.
|
|
216
|
+
//
|
|
217
|
+
// The gate used to apply to both, and every `underpost` invocation loads the host
|
|
218
|
+
// configuration store with `override: true` — on a provisioned node that store carries
|
|
219
|
+
// NODE_ENV=production, so the gate was false for every CLI call and a named sub-conf was
|
|
220
|
+
// silently discarded in favour of the base conf.
|
|
221
|
+
if (explicit || process.env.NODE_ENV === 'development') {
|
|
222
|
+
const devConfPath = `${folder}/conf.${confType}.dev.${effectiveSubConf}.json`;
|
|
223
|
+
if (fs.existsSync(devConfPath)) return devConfPath;
|
|
224
|
+
}
|
|
254
225
|
}
|
|
255
226
|
return `${folder}/conf.${confType}.json`;
|
|
256
227
|
};
|
|
@@ -280,13 +251,6 @@ const readConfJson = (deployId, confType, options = {}) => {
|
|
|
280
251
|
return parsed;
|
|
281
252
|
};
|
|
282
253
|
|
|
283
|
-
/**
|
|
284
|
-
* Default deploy ID used when no deploy ID is specified.
|
|
285
|
-
* @constant {string}
|
|
286
|
-
* @memberof ServerConfBuilder
|
|
287
|
-
*/
|
|
288
|
-
const DEFAULT_DEPLOY_ID = 'dd-default';
|
|
289
|
-
|
|
290
254
|
/**
|
|
291
255
|
* @class Config
|
|
292
256
|
* @description Manages the configuration of the server.
|
|
@@ -317,7 +281,7 @@ const Config = {
|
|
|
317
281
|
deployContext = process.env.DEPLOY_ID;
|
|
318
282
|
if (!subConf && process.argv[3] && typeof process.argv[3] === 'string') subConf = process.argv[3];
|
|
319
283
|
|
|
320
|
-
|
|
284
|
+
latchAwaitDeploy();
|
|
321
285
|
if (deployContext.startsWith('dd-')) loadConf(deployContext, subConf);
|
|
322
286
|
if (deployContext === 'proxy') await Config.buildProxy(deployList, subConf);
|
|
323
287
|
},
|
|
@@ -325,16 +289,15 @@ const Config = {
|
|
|
325
289
|
* @method deployIdFactory
|
|
326
290
|
* @description Creates a new deploy ID.
|
|
327
291
|
* @param {string} [deployId='dd-default']
|
|
328
|
-
* @param {object} [options={ subConf: ''
|
|
292
|
+
* @param {object} [options={ subConf: '' }] - The options.
|
|
329
293
|
* @memberof ServerConfBuilder
|
|
330
294
|
*/
|
|
331
|
-
deployIdFactory: function (deployId = DEFAULT_DEPLOY_ID, options = { subConf: ''
|
|
295
|
+
deployIdFactory: function (deployId = DEFAULT_DEPLOY_ID, options = { subConf: '' }) {
|
|
332
296
|
if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
|
|
333
297
|
|
|
334
298
|
logger.info('Build deployId', deployId);
|
|
335
299
|
|
|
336
300
|
const folder = `./engine-private/conf/${deployId}`;
|
|
337
|
-
const repoName = `engine-${deployId.split('dd-')[1]}`;
|
|
338
301
|
|
|
339
302
|
if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true });
|
|
340
303
|
|
|
@@ -386,29 +349,6 @@ const Config = {
|
|
|
386
349
|
);
|
|
387
350
|
}
|
|
388
351
|
|
|
389
|
-
if (options.cluster === true) {
|
|
390
|
-
fs.writeFileSync(
|
|
391
|
-
`./.github/workflows/${repoName}.cd.yml`,
|
|
392
|
-
fs.readFileSync(`./.github/workflows/engine-test.cd.yml`, 'utf8').replaceAll('test', deployId.split('dd-')[1]),
|
|
393
|
-
'utf8',
|
|
394
|
-
);
|
|
395
|
-
fs.writeFileSync(
|
|
396
|
-
`./.github/workflows/${repoName}.ci.yml`,
|
|
397
|
-
fs.readFileSync(`./.github/workflows/engine-test.ci.yml`, 'utf8').replaceAll('test', deployId.split('dd-')[1]),
|
|
398
|
-
'utf8',
|
|
399
|
-
);
|
|
400
|
-
shellExec(`node bin new --default-conf --deploy-id ${deployId}`);
|
|
401
|
-
|
|
402
|
-
if (!fs.existsSync(`./engine-private/deploy/dd.router`))
|
|
403
|
-
fs.writeFileSync(`./engine-private/deploy/dd.router`, deployId, 'utf8');
|
|
404
|
-
else
|
|
405
|
-
fs.writeFileSync(
|
|
406
|
-
`./engine-private/deploy/dd.router`,
|
|
407
|
-
fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').trim() + `,${deployId}`,
|
|
408
|
-
'utf8',
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
352
|
return { deployIdFolder: folder, deployId };
|
|
413
353
|
},
|
|
414
354
|
/**
|
|
@@ -468,17 +408,178 @@ const Config = {
|
|
|
468
408
|
* @param {string} [subConf=''] - The sub configuration.
|
|
469
409
|
* @memberof ServerConfBuilder
|
|
470
410
|
*/
|
|
411
|
+
/**
|
|
412
|
+
* @method deployEnvFilePath
|
|
413
|
+
* @description Resolves a deployment's env file for an environment.
|
|
414
|
+
*
|
|
415
|
+
* A named sub-configuration selects `.env.<env>.<subConf>` when that file exists and degrades to
|
|
416
|
+
* the plain `.env.<env>` when it does not. Single source of this precedence: {@link loadConf}
|
|
417
|
+
* materializes the working tree from it and {@link UnderpostApp} addresses it from the CLI, so
|
|
418
|
+
* both always agree on which file is in effect.
|
|
419
|
+
* @param {string} deployId - Deployment id.
|
|
420
|
+
* @param {string} [env='production'] - Environment selector.
|
|
421
|
+
* @param {string} [subConf] - Sub-configuration name.
|
|
422
|
+
* @returns {string} Path to the deployment env file.
|
|
423
|
+
* @memberof ServerConfBuilder
|
|
424
|
+
*/
|
|
425
|
+
const deployEnvFilePath = (deployId, env = 'production', subConf = '') => {
|
|
426
|
+
const base = `${getConfFolder(deployId)}/.env.${env || 'production'}`;
|
|
427
|
+
const name = `${subConf ?? ''}`.trim();
|
|
428
|
+
if (!name) return base;
|
|
429
|
+
const scoped = `${base}.${name}`;
|
|
430
|
+
return fs.existsSync(scoped) ? scoped : base;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* The environments a deployment's configuration actually carries.
|
|
435
|
+
*
|
|
436
|
+
* Read from the tree rather than assumed, because which environments exist is a property of the
|
|
437
|
+
* checkout: a workstation carries every one a developer runs, and a workload carries none at all —
|
|
438
|
+
* its environment is injected. A name is the segment after `.env.` and nothing further, so an
|
|
439
|
+
* overlay (`.env.production.oci`) and a sub-configuration (`.env.development.api`) are the
|
|
440
|
+
* variants of an environment, never environments of their own.
|
|
441
|
+
* @param {string} deployId - Deployment id.
|
|
442
|
+
* @returns {string[]} Environment names present, sorted.
|
|
443
|
+
* @memberof ServerConfBuilder
|
|
444
|
+
*/
|
|
445
|
+
const deployEnvNamesFactory = (deployId) => {
|
|
446
|
+
const folder = getConfFolder(deployId);
|
|
447
|
+
if (!fs.existsSync(folder)) return [];
|
|
448
|
+
return fs
|
|
449
|
+
.readdirSync(folder)
|
|
450
|
+
.map((entry) => `${entry}`.match(/^\.env\.([^.]+)$/)?.[1])
|
|
451
|
+
.filter(Boolean)
|
|
452
|
+
.sort();
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Resolves a custom instance's env file for an environment.
|
|
457
|
+
*
|
|
458
|
+
* An instance's runtime environment is its own file, not a sub-configuration of the parent
|
|
459
|
+
* deployment's: `instance-build-manifest` writes it, the instance container sources it at boot,
|
|
460
|
+
* and {@link UnderpostApp} addresses it from the CLI through `--args instance-id=`. Single
|
|
461
|
+
* source of that path, so the three can never disagree.
|
|
462
|
+
* @param {string} deployId - Parent deployment id.
|
|
463
|
+
* @param {string} instanceId - Instance id, as declared in `conf.instances.json`.
|
|
464
|
+
* @param {string} [env='production'] - Environment selector.
|
|
465
|
+
* @returns {string} Path to the instance env file.
|
|
466
|
+
* @memberof ServerConfBuilder
|
|
467
|
+
*/
|
|
468
|
+
const instanceEnvFilePath = (deployId, instanceId, env = 'production') =>
|
|
469
|
+
`${getConfFolder(deployId)}/instances/${instanceId}/env/${env || 'production'}.env`;
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Suffix marking a deployment env file's OCI (container) runtime overlay.
|
|
473
|
+
* @constant {string}
|
|
474
|
+
* @memberof ServerConfBuilder
|
|
475
|
+
*/
|
|
476
|
+
const OCI_ENV_SUFFIX = '.oci';
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* @method deployOciEnvFilePath
|
|
480
|
+
* @description Resolves a deployment's OCI runtime env overlay for an environment.
|
|
481
|
+
*
|
|
482
|
+
* The overlay is per environment, not per sub-configuration: it carries the values that only
|
|
483
|
+
* hold when the deployment runs as a container image (cluster-internal service names, in-cluster
|
|
484
|
+
* database endpoints), which are the same whichever host sub-configuration is in effect.
|
|
485
|
+
* @param {string} deployId - Deployment id.
|
|
486
|
+
* @param {string} [env='production'] - Environment selector.
|
|
487
|
+
* @returns {string} Path to the deployment's OCI env overlay.
|
|
488
|
+
* @memberof ServerConfBuilder
|
|
489
|
+
*/
|
|
490
|
+
const deployOciEnvFilePath = (deployId, env = 'production') =>
|
|
491
|
+
`${getConfFolder(deployId)}/.env.${env || 'production'}${OCI_ENV_SUFFIX}`;
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* @method envLineKey
|
|
495
|
+
* @description The variable name a dotenv line declares, or `''` for blanks and comments.
|
|
496
|
+
* @param {string} line - One dotenv line.
|
|
497
|
+
* @returns {string} Declared key, or `''`.
|
|
498
|
+
* @memberof ServerConfBuilder
|
|
499
|
+
*/
|
|
500
|
+
const envLineKey = (line) => {
|
|
501
|
+
const trimmed = `${line}`.trimStart();
|
|
502
|
+
if (!trimmed || trimmed.startsWith('#')) return '';
|
|
503
|
+
const assign = trimmed.indexOf('=');
|
|
504
|
+
return assign === -1 ? '' : trimmed.slice(0, assign).trim();
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* @method ociEnvContentFactory
|
|
509
|
+
* @description Applies an OCI overlay onto base env contents.
|
|
510
|
+
*
|
|
511
|
+
* Textual rather than parse-and-render: base lines the overlay does not redeclare survive
|
|
512
|
+
* verbatim — comments, ordering, and value quoting included — and the overlay is appended whole,
|
|
513
|
+
* so an overridden key appears exactly once with the container value.
|
|
514
|
+
* @param {string} baseContent - The deployment's `.env.<env>` contents.
|
|
515
|
+
* @param {string} overlayContent - The matching `.env.<env>.oci` contents.
|
|
516
|
+
* @returns {string} Overlay-applied env contents.
|
|
517
|
+
* @memberof ServerConfBuilder
|
|
518
|
+
*/
|
|
519
|
+
const ociEnvContentFactory = (baseContent, overlayContent) => {
|
|
520
|
+
const overlay = `${overlayContent ?? ''}`.trim();
|
|
521
|
+
if (!overlay) return baseContent;
|
|
522
|
+
const overridden = new Set(overlay.split('\n').map(envLineKey).filter(Boolean));
|
|
523
|
+
const kept = `${baseContent ?? ''}`
|
|
524
|
+
.split('\n')
|
|
525
|
+
.filter((line) => {
|
|
526
|
+
const key = envLineKey(line);
|
|
527
|
+
return !key || !overridden.has(key);
|
|
528
|
+
})
|
|
529
|
+
.join('\n')
|
|
530
|
+
.trimEnd();
|
|
531
|
+
return `${kept}${kept ? '\n' : ''}${overlay}\n`;
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* @method deployEnvContentFactory
|
|
536
|
+
* @description Reads a deployment's env file for an environment, with its OCI overlay applied
|
|
537
|
+
* when one exists and the caller is resolving for a container runtime.
|
|
538
|
+
*
|
|
539
|
+
* Single source of the overlay precedence: {@link loadConf} materializes the working tree from
|
|
540
|
+
* it and {@link UnderpostApp} projects the cluster Secret from it, so the file a container reads
|
|
541
|
+
* and the Secret injected into it always carry the same values. Outside a container the base
|
|
542
|
+
* file is returned untouched, which is what keeps a developer host on its local endpoints.
|
|
543
|
+
* @param {string} deployId - Deployment id.
|
|
544
|
+
* @param {string} [env='production'] - Environment selector.
|
|
545
|
+
* @param {string} [subConf=''] - Sub-configuration name.
|
|
546
|
+
* @param {object} [options] - Resolution options.
|
|
547
|
+
* @param {boolean} [options.oci] - Force the overlay on or off; defaults to container detection.
|
|
548
|
+
* @returns {{source: string, overlay: string|null, content: string, values: Object<string,string>}}
|
|
549
|
+
* The base file used, the overlay applied (or `null`), and the resulting contents and values.
|
|
550
|
+
* @memberof ServerConfBuilder
|
|
551
|
+
*/
|
|
552
|
+
const deployEnvContentFactory = (deployId, env = 'production', subConf = '', options = {}) => {
|
|
553
|
+
const source = deployEnvFilePath(deployId, env, subConf);
|
|
554
|
+
const baseContent = fs.readFileSync(source, 'utf8');
|
|
555
|
+
const overlayPath = deployOciEnvFilePath(deployId, env);
|
|
556
|
+
const applyOverlay = (options.oci ?? isOciRuntime()) && fs.existsSync(overlayPath);
|
|
557
|
+
if (!applyOverlay) return { source, overlay: null, content: baseContent, values: dotenv.parse(baseContent) };
|
|
558
|
+
const content = ociEnvContentFactory(baseContent, fs.readFileSync(overlayPath, 'utf8'));
|
|
559
|
+
return { source, overlay: overlayPath, content, values: dotenv.parse(content) };
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* @method cleanDeployEnvFiles
|
|
564
|
+
* @description Removes the working-tree env files {@link loadConf} materializes.
|
|
565
|
+
* Single source of the file list, shared with the app domain's `clean` action.
|
|
566
|
+
* @returns {Array<string>} The paths that were present and removed.
|
|
567
|
+
* @memberof ServerConfBuilder
|
|
568
|
+
*/
|
|
569
|
+
const cleanDeployEnvFiles = () => {
|
|
570
|
+
const targets = ['./.env', './.env.production', './.env.development', './.env.test'];
|
|
571
|
+
const present = targets.filter((target) => fs.existsSync(target));
|
|
572
|
+
for (const target of present) fs.removeSync(target);
|
|
573
|
+
return present;
|
|
574
|
+
};
|
|
575
|
+
|
|
471
576
|
const loadConf = (deployId = DEFAULT_DEPLOY_ID, subConf) => {
|
|
472
577
|
if (deployId === 'current') {
|
|
473
578
|
console.log(process.env.DEPLOY_ID);
|
|
474
579
|
return;
|
|
475
580
|
}
|
|
476
581
|
if (deployId === 'clean') {
|
|
477
|
-
|
|
478
|
-
fs.removeSync(`${path}/.env`);
|
|
479
|
-
fs.removeSync(`${path}/.env.production`);
|
|
480
|
-
fs.removeSync(`${path}/.env.development`);
|
|
481
|
-
fs.removeSync(`${path}/.env.test`);
|
|
582
|
+
cleanDeployEnvFiles();
|
|
482
583
|
return;
|
|
483
584
|
}
|
|
484
585
|
const folder = getConfFolder(deployId);
|
|
@@ -488,34 +589,58 @@ const loadConf = (deployId = DEFAULT_DEPLOY_ID, subConf) => {
|
|
|
488
589
|
if (subConf) process.env.DEPLOY_SUB_CONF = subConf;
|
|
489
590
|
|
|
490
591
|
for (const typeConf of Object.keys(Config.default)) {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
592
|
+
const confPath = `${folder}/conf.${typeConf}.json`;
|
|
593
|
+
let srcConf;
|
|
594
|
+
if (fs.existsSync(confPath)) srcConf = fs.readFileSync(confPath, 'utf8');
|
|
595
|
+
else {
|
|
596
|
+
Config.default[typeConf] = newInstance(DefaultConf[typeConf]);
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
// Same precedence as getConfFilePath, so loadConf and every later read agree on which file
|
|
600
|
+
// is in effect: a sub-conf named here was named by the caller, so it is not gated on the
|
|
601
|
+
// ambient environment.
|
|
602
|
+
if (typeConf === 'server' && subConf) {
|
|
603
|
+
const devConfPath = getConfFilePath(deployId, typeConf, subConf);
|
|
494
604
|
if (fs.existsSync(devConfPath)) srcConf = fs.readFileSync(devConfPath, 'utf8');
|
|
495
605
|
}
|
|
496
606
|
let parsed = JSON.parse(srcConf);
|
|
497
607
|
if (typeConf === 'server') parsed = loadReplicas(deployId, parsed);
|
|
498
608
|
Config.default[typeConf] = parsed;
|
|
499
609
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
610
|
+
// Each materialized copy carries its own OCI overlay, so a container that later switches
|
|
611
|
+
// NODE_ENV still reads container values rather than the host endpoints of the base file.
|
|
612
|
+
const available = deployEnvNamesFactory(deployId);
|
|
613
|
+
for (const name of available)
|
|
614
|
+
fs.writeFileSync(`./.env.${name}`, deployEnvContentFactory(deployId, name).content, 'utf8');
|
|
615
|
+
|
|
616
|
+
const envName = process.env.NODE_ENV || 'development';
|
|
617
|
+
// A tree with no environment of its own is a workload: its configuration is injected, and
|
|
618
|
+
// materializing is neither possible nor wanted. A tree that carries environments but not this
|
|
619
|
+
// one is a mistake worth naming — falling through would run the deployment against whichever
|
|
620
|
+
// environment happened to be loaded.
|
|
621
|
+
if (available.length > 0) {
|
|
622
|
+
if (!available.includes(envName))
|
|
623
|
+
throw new Error(
|
|
624
|
+
configRejectionFactory({
|
|
625
|
+
domain: 'app',
|
|
626
|
+
deployId,
|
|
627
|
+
env: envName,
|
|
628
|
+
key: 'NODE_ENV',
|
|
629
|
+
reason: `no environment source; ${getConfFolder(deployId)} carries ${available.join(', ')}`,
|
|
630
|
+
}),
|
|
631
|
+
);
|
|
632
|
+
const { content, values } = deployEnvContentFactory(deployId, envName, subConf);
|
|
633
|
+
fs.writeFileSync(`./.env`, content, 'utf8');
|
|
634
|
+
process.env = { ...process.env, ...values, NODE_ENV: envName };
|
|
635
|
+
} else process.env.NODE_ENV = envName;
|
|
515
636
|
const originPackageJson = JSON.parse(fs.readFileSync(`./package.json`, 'utf8'));
|
|
516
637
|
const packageJson = JSON.parse(fs.readFileSync(`${folder}/package.json`, 'utf8'));
|
|
517
638
|
originPackageJson.scripts.start = packageJson.scripts.start;
|
|
518
639
|
packageJson.scripts = originPackageJson.scripts;
|
|
640
|
+
// `bin` is engine identity, not deploy identity: it is what `npm link` publishes as the
|
|
641
|
+
// global `underpost` command. Deploy manifests do not carry one, so without this the
|
|
642
|
+
// checkout would stop being linkable the moment its environment was loaded.
|
|
643
|
+
if (originPackageJson.bin) packageJson.bin = originPackageJson.bin;
|
|
519
644
|
fs.writeFileSync(`./package.json`, JSON.stringify(packageJson, null, 4), 'utf8');
|
|
520
645
|
return { folder, deployId };
|
|
521
646
|
};
|
|
@@ -886,247 +1011,6 @@ const cloneSrcComponents = async ({ toOptions, fromOptions }) => {
|
|
|
886
1011
|
}
|
|
887
1012
|
};
|
|
888
1013
|
|
|
889
|
-
/**
|
|
890
|
-
* @method buildProxyRouter
|
|
891
|
-
* @description Builds the proxy router.
|
|
892
|
-
* @memberof ServerConfBuilder
|
|
893
|
-
*/
|
|
894
|
-
const buildProxyRouter = () => {
|
|
895
|
-
const confServer = newInstance(Config.default.server);
|
|
896
|
-
let currentPort = parseInt(process.env.PORT) + 1;
|
|
897
|
-
const proxyRouter = {};
|
|
898
|
-
for (const host of Object.keys(confServer)) {
|
|
899
|
-
for (const path of Object.keys(confServer[host])) {
|
|
900
|
-
if (confServer[host][path].singleReplica) continue;
|
|
901
|
-
|
|
902
|
-
if (isDevProxyContext()) confServer[host][path].proxy = [isTlsDevProxy() ? 443 : 80];
|
|
903
|
-
|
|
904
|
-
confServer[host][path].port = newInstance(currentPort);
|
|
905
|
-
for (const port of confServer[host][path].proxy) {
|
|
906
|
-
if (!(port in proxyRouter)) proxyRouter[port] = {};
|
|
907
|
-
proxyRouter[port][`${host}${path}`] = {
|
|
908
|
-
// target: `http://${host}:${confServer[host][path].port}${path}`,
|
|
909
|
-
target: `http://localhost:${confServer[host][path].port}`,
|
|
910
|
-
// target: `http://127.0.0.1:${confServer[host][path].port}`,
|
|
911
|
-
proxy: confServer[host][path].proxy,
|
|
912
|
-
redirect: confServer[host][path].redirect,
|
|
913
|
-
host,
|
|
914
|
-
path,
|
|
915
|
-
};
|
|
916
|
-
}
|
|
917
|
-
currentPort++;
|
|
918
|
-
if (confServer[host][path].peer) {
|
|
919
|
-
const peerPath = path === '/' ? `/peer` : `${path}/peer`;
|
|
920
|
-
confServer[host][peerPath] = newInstance(confServer[host][path]);
|
|
921
|
-
confServer[host][peerPath].port = newInstance(currentPort);
|
|
922
|
-
for (const port of confServer[host][path].proxy) {
|
|
923
|
-
if (!(port in proxyRouter)) proxyRouter[port] = {};
|
|
924
|
-
proxyRouter[port][`${host}${peerPath}`] = {
|
|
925
|
-
// target: `http://${host}:${confServer[host][peerPath].port}${peerPath}`,
|
|
926
|
-
target: `http://localhost:${confServer[host][peerPath].port}`,
|
|
927
|
-
// target: `http://127.0.0.1:${confServer[host][peerPath].port}`,
|
|
928
|
-
proxy: confServer[host][peerPath].proxy,
|
|
929
|
-
host,
|
|
930
|
-
path: peerPath,
|
|
931
|
-
};
|
|
932
|
-
}
|
|
933
|
-
currentPort++;
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
return proxyRouter;
|
|
939
|
-
};
|
|
940
|
-
|
|
941
|
-
/**
|
|
942
|
-
* @method pathPortAssignmentFactory
|
|
943
|
-
* @description Creates the path port assignment.
|
|
944
|
-
* @param {string} deployId - The deploy ID.
|
|
945
|
-
* @param {object} router - The router.
|
|
946
|
-
* @param {object} confServer - The server configuration.
|
|
947
|
-
* @memberof ServerConfBuilder
|
|
948
|
-
*/
|
|
949
|
-
const pathPortAssignmentFactory = async (deployId, router, confServer) => {
|
|
950
|
-
const pathPortAssignmentData = {};
|
|
951
|
-
for (const host of Object.keys(confServer)) {
|
|
952
|
-
const pathPortAssignment = [];
|
|
953
|
-
for (const path of Object.keys(confServer[host])) {
|
|
954
|
-
const { peer } = confServer[host][path];
|
|
955
|
-
if (!router[`${host}${path === '/' ? '' : path}`]) continue;
|
|
956
|
-
const port = parseInt(router[`${host}${path === '/' ? '' : path}`].split(':')[2]);
|
|
957
|
-
// logger.info('', { host, port, path });
|
|
958
|
-
pathPortAssignment.push({
|
|
959
|
-
port,
|
|
960
|
-
path,
|
|
961
|
-
});
|
|
962
|
-
|
|
963
|
-
if (peer) {
|
|
964
|
-
// logger.info('', { host, port: port + 1, path: '/peer' });
|
|
965
|
-
pathPortAssignment.push({
|
|
966
|
-
port: port + 1,
|
|
967
|
-
path: `${path === '/' ? '' : path}/peer`,
|
|
968
|
-
});
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
pathPortAssignmentData[host] = pathPortAssignment;
|
|
972
|
-
}
|
|
973
|
-
if (fs.existsSync(`./engine-private/replica`)) {
|
|
974
|
-
const singleReplicas = await fs.readdir(`./engine-private/replica`);
|
|
975
|
-
for (let replica of singleReplicas) {
|
|
976
|
-
if (replica.startsWith(deployId)) {
|
|
977
|
-
const replicaServerConf = loadConfServerJson(`./engine-private/replica/${replica}/conf.server.json`);
|
|
978
|
-
for (const host of Object.keys(replicaServerConf)) {
|
|
979
|
-
const pathPortAssignment = [];
|
|
980
|
-
for (const path of Object.keys(replicaServerConf[host])) {
|
|
981
|
-
const { peer } = replicaServerConf[host][path];
|
|
982
|
-
if (!router[`${host}${path === '/' ? '' : path}`]) continue;
|
|
983
|
-
const port = parseInt(router[`${host}${path === '/' ? '' : path}`].split(':')[2]);
|
|
984
|
-
// logger.info('', { host, port, path });
|
|
985
|
-
pathPortAssignment.push({
|
|
986
|
-
port,
|
|
987
|
-
path,
|
|
988
|
-
});
|
|
989
|
-
|
|
990
|
-
if (peer) {
|
|
991
|
-
// logger.info('', { host, port: port + 1, path: '/peer' });
|
|
992
|
-
pathPortAssignment.push({
|
|
993
|
-
port: port + 1,
|
|
994
|
-
path: `${path === '/' ? '' : path}/peer`,
|
|
995
|
-
});
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
pathPortAssignmentData[host] = pathPortAssignmentData[host].concat(pathPortAssignment);
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
return pathPortAssignmentData;
|
|
1004
|
-
};
|
|
1005
|
-
|
|
1006
|
-
/**
|
|
1007
|
-
* @method deployRangePortFactory
|
|
1008
|
-
* @description Creates the deploy range port factory.
|
|
1009
|
-
* @param {object} router - The router.
|
|
1010
|
-
* @returns {object} - The deploy range port factory.
|
|
1011
|
-
* @memberof ServerConfBuilder
|
|
1012
|
-
*/
|
|
1013
|
-
const deployRangePortFactory = (router) => {
|
|
1014
|
-
const ports = Object.values(router).map((p) => parseInt(p.split(':')[2]));
|
|
1015
|
-
const fromPort = Math.min(...ports);
|
|
1016
|
-
const toPort = Math.max(...ports);
|
|
1017
|
-
return { ports, fromPort, toPort };
|
|
1018
|
-
};
|
|
1019
|
-
|
|
1020
|
-
/**
|
|
1021
|
-
* @method buildKindPorts
|
|
1022
|
-
* @description Builds the kind ports.
|
|
1023
|
-
* @param {number} from - The from port.
|
|
1024
|
-
* @param {number} to - The to port.
|
|
1025
|
-
* @returns {string} - The kind ports.
|
|
1026
|
-
* @memberof ServerConfBuilder
|
|
1027
|
-
*/
|
|
1028
|
-
const buildKindPorts = (from, to) =>
|
|
1029
|
-
range(parseInt(from), parseInt(to))
|
|
1030
|
-
.map(
|
|
1031
|
-
(port) => ` - name: 'tcp-${port}'
|
|
1032
|
-
protocol: TCP
|
|
1033
|
-
port: ${port}
|
|
1034
|
-
targetPort: ${port}
|
|
1035
|
-
- name: 'udp-${port}'
|
|
1036
|
-
protocol: UDP
|
|
1037
|
-
port: ${port}
|
|
1038
|
-
targetPort: ${port}
|
|
1039
|
-
`,
|
|
1040
|
-
)
|
|
1041
|
-
.join('\n');
|
|
1042
|
-
|
|
1043
|
-
/**
|
|
1044
|
-
* @method buildPortProxyRouter
|
|
1045
|
-
* @description Builds the port proxy router.
|
|
1046
|
-
* @param {object} options - The options.
|
|
1047
|
-
* @param {number} [options.port=4000] - The port.
|
|
1048
|
-
* @param {object} options.proxyRouter - The proxy router.
|
|
1049
|
-
* @param {object} [options.hosts] - The hosts.
|
|
1050
|
-
* @param {boolean} [options.orderByPathLength=false] - Whether to order by path length.
|
|
1051
|
-
* @param {boolean} [options.devProxyContext=false] - Whether to use dev proxy context.
|
|
1052
|
-
* @returns {object} - The port proxy router.
|
|
1053
|
-
* @memberof ServerConfBuilder
|
|
1054
|
-
*/
|
|
1055
|
-
const buildPortProxyRouter = (
|
|
1056
|
-
options = { port: 4000, proxyRouter, hosts, orderByPathLength: false, devProxyContext: false },
|
|
1057
|
-
) => {
|
|
1058
|
-
let { port, proxyRouter, hosts, orderByPathLength } = options;
|
|
1059
|
-
hosts = hosts || proxyRouter[port] || {};
|
|
1060
|
-
|
|
1061
|
-
const router = {};
|
|
1062
|
-
// build router
|
|
1063
|
-
Object.keys(hosts).map((hostKey) => {
|
|
1064
|
-
let { host, path, target, proxy, peer } = hosts[hostKey];
|
|
1065
|
-
|
|
1066
|
-
if (!proxy.includes(port)) {
|
|
1067
|
-
logger.warn('Proxy port not set on conf', { port, host, path, proxy, target });
|
|
1068
|
-
if (process.env.NODE_ENV === 'production') {
|
|
1069
|
-
logger.warn('Omitting host', { host, path, target });
|
|
1070
|
-
return;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
// ${process.env.NODE_ENV === 'development' && !isDevProxyContext() ? `:${port}` : ''}
|
|
1074
|
-
const absoluteHost = [80, 443].includes(port)
|
|
1075
|
-
? `${host}${path === '/' ? '' : path}`
|
|
1076
|
-
: `${host}:${port}${path === '/' ? '' : path}`;
|
|
1077
|
-
|
|
1078
|
-
if (absoluteHost in router)
|
|
1079
|
-
logger.warn('Overwrite: Absolute host already exists on router', { absoluteHost, target });
|
|
1080
|
-
|
|
1081
|
-
if (options.devProxyContext === true) {
|
|
1082
|
-
const appDevPort = parseInt(target.split(':')[2]) - process.env.DEV_PROXY_PORT_OFFSET;
|
|
1083
|
-
router[absoluteHost] = `http://localhost:${appDevPort}`;
|
|
1084
|
-
} else router[absoluteHost] = target;
|
|
1085
|
-
}); // order router
|
|
1086
|
-
|
|
1087
|
-
if (Object.keys(router).length === 0) return router;
|
|
1088
|
-
|
|
1089
|
-
const devApiConfPath = `./engine-private/conf/${process.argv[3]}/conf.server.dev.${process.argv[4]}-dev-api.json`;
|
|
1090
|
-
if (options.devProxyContext === true && process.env.NODE_ENV === 'development' && fs.existsSync(devApiConfPath)) {
|
|
1091
|
-
const confDevApiServer = JSON.parse(fs.readFileSync(devApiConfPath, 'utf8'));
|
|
1092
|
-
let devApiHosts = [];
|
|
1093
|
-
let origins = [];
|
|
1094
|
-
for (const _host of Object.keys(confDevApiServer))
|
|
1095
|
-
for (const _path of Object.keys(confDevApiServer[_host])) {
|
|
1096
|
-
if (confDevApiServer[_host][_path].origins && confDevApiServer[_host][_path].origins.length) {
|
|
1097
|
-
origins.push(...confDevApiServer[_host][_path].origins);
|
|
1098
|
-
if (_path !== 'peer' && devApiHosts.length === 0)
|
|
1099
|
-
devApiHosts.push(
|
|
1100
|
-
`${_host}${[80, 443].includes(port) && isDevProxyContext() ? '' : `:${port}`}${_path == '/' ? '' : _path}`,
|
|
1101
|
-
);
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
origins = Array.from(new Set(origins));
|
|
1105
|
-
console.log({
|
|
1106
|
-
origins,
|
|
1107
|
-
devApiHosts,
|
|
1108
|
-
});
|
|
1109
|
-
for (const devApiHost of devApiHosts) {
|
|
1110
|
-
if (devApiHost in router) {
|
|
1111
|
-
const target = router[devApiHost];
|
|
1112
|
-
delete router[devApiHost];
|
|
1113
|
-
router[`${devApiHost}/${process.env.BASE_API}`] = target;
|
|
1114
|
-
router[`${devApiHost}/socket.io`] = target;
|
|
1115
|
-
for (const origin of origins) router[devApiHost] = origin;
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
if (orderByPathLength === true) {
|
|
1121
|
-
const reOrderRouter = {};
|
|
1122
|
-
for (const absoluteHostKey of orderArrayFromAttrInt(Object.keys(router), 'length'))
|
|
1123
|
-
reOrderRouter[absoluteHostKey] = router[absoluteHostKey];
|
|
1124
|
-
return reOrderRouter;
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
return router;
|
|
1128
|
-
};
|
|
1129
|
-
|
|
1130
1014
|
/**
|
|
1131
1015
|
* @method buildReplicaId
|
|
1132
1016
|
* @description Builds the replica ID.
|
|
@@ -1154,17 +1038,7 @@ const getDataDeploy = async (
|
|
|
1154
1038
|
disableSyncEnvPort: false,
|
|
1155
1039
|
},
|
|
1156
1040
|
) => {
|
|
1157
|
-
let dataDeploy =
|
|
1158
|
-
.readFileSync(`./engine-private/deploy/dd.router`, 'utf8')
|
|
1159
|
-
.split(',')
|
|
1160
|
-
.map((deployId) => deployId.trim())
|
|
1161
|
-
.filter((deployId) => deployId);
|
|
1162
|
-
|
|
1163
|
-
dataDeploy = dataDeploy.map((deployId) => {
|
|
1164
|
-
return {
|
|
1165
|
-
deployId,
|
|
1166
|
-
};
|
|
1167
|
-
});
|
|
1041
|
+
let dataDeploy = readDeployRoutes().map((deployId) => ({ deployId }));
|
|
1168
1042
|
|
|
1169
1043
|
if (options && options.buildSingleReplica && fs.existsSync(`./engine-private/replica`))
|
|
1170
1044
|
fs.removeSync(`./engine-private/replica`);
|
|
@@ -1181,7 +1055,7 @@ const getDataDeploy = async (
|
|
|
1181
1055
|
for (const path of Object.keys(serverConf[host])) {
|
|
1182
1056
|
if (!isReplicaDeploy && serverConf[host][path].replicas && serverConf[host][path].singleReplica) {
|
|
1183
1057
|
if (options && options.buildSingleReplica)
|
|
1184
|
-
await Underpost.
|
|
1058
|
+
await Underpost.client.callback(deployObj.deployId, '', host, path, {
|
|
1185
1059
|
singleReplica: true,
|
|
1186
1060
|
});
|
|
1187
1061
|
replicaDataDeploy = replicaDataDeploy.concat(
|
|
@@ -1199,7 +1073,7 @@ const getDataDeploy = async (
|
|
|
1199
1073
|
}
|
|
1200
1074
|
|
|
1201
1075
|
if (!options.disableSyncEnvPort && options.buildSingleReplica)
|
|
1202
|
-
await Underpost.
|
|
1076
|
+
await Underpost.client.callback(undefined, '', '', '', { syncEnvPort: true });
|
|
1203
1077
|
|
|
1204
1078
|
logger.info('Deployments configured', buildDataDeploy);
|
|
1205
1079
|
|
|
@@ -1224,7 +1098,7 @@ const validateTemplatePath = (absolutePath = '') => {
|
|
|
1224
1098
|
const clients = DefaultConf.client.default.services;
|
|
1225
1099
|
|
|
1226
1100
|
if (
|
|
1227
|
-
absolutePath.match('src/api') &&
|
|
1101
|
+
absolutePath.match('src/api/') &&
|
|
1228
1102
|
!absolutePath.match('src/api/types.js') &&
|
|
1229
1103
|
!confServer.apis.find((p) => absolutePath.match(`src/api/${p}/`))
|
|
1230
1104
|
) {
|
|
@@ -1299,11 +1173,11 @@ const validateTemplatePath = (absolutePath = '') => {
|
|
|
1299
1173
|
* @memberof ServerConfBuilder
|
|
1300
1174
|
*/
|
|
1301
1175
|
const awaitDeployMonitor = async (isFinal = false, deltaMs = 1000, callback = false) => {
|
|
1302
|
-
if (!callback)
|
|
1176
|
+
if (!callback) latchAwaitDeploy();
|
|
1303
1177
|
if (isFinal) logger.info('Final deployment running (no replica)');
|
|
1304
1178
|
await timer(deltaMs);
|
|
1305
|
-
if (
|
|
1306
|
-
if (
|
|
1179
|
+
if (getRuntimeStatus() === RUNTIME_STATUS.ERROR) return false;
|
|
1180
|
+
if (isAwaitingDeploy()) return await awaitDeployMonitor(false, deltaMs, true);
|
|
1307
1181
|
return true;
|
|
1308
1182
|
};
|
|
1309
1183
|
|
|
@@ -1431,43 +1305,6 @@ const generateSecurePassword = (length = 16) => {
|
|
|
1431
1305
|
return chars.join('');
|
|
1432
1306
|
};
|
|
1433
1307
|
|
|
1434
|
-
/**
|
|
1435
|
-
* @method getNpmRootPath
|
|
1436
|
-
* @description Gets the npm root path.
|
|
1437
|
-
* @returns {string} - The npm root path.
|
|
1438
|
-
* @memberof ServerConfBuilder
|
|
1439
|
-
*/
|
|
1440
|
-
const getNpmRootPath = () =>
|
|
1441
|
-
shellExec(`npm root -g`, {
|
|
1442
|
-
stdout: true,
|
|
1443
|
-
disableLog: true,
|
|
1444
|
-
silent: true,
|
|
1445
|
-
}).trim();
|
|
1446
|
-
|
|
1447
|
-
/**
|
|
1448
|
-
* @method getUnderpostRootPath
|
|
1449
|
-
* @description Gets the underpost root path.
|
|
1450
|
-
* @returns {string} - The underpost root path.
|
|
1451
|
-
* @memberof ServerConfBuilder
|
|
1452
|
-
*/
|
|
1453
|
-
const getUnderpostRootPath = () => `${getNpmRootPath()}/underpost`;
|
|
1454
|
-
|
|
1455
|
-
/**
|
|
1456
|
-
* @method writeEnv
|
|
1457
|
-
* @description Writes the environment variables.
|
|
1458
|
-
* @param {string} envPath - The environment path.
|
|
1459
|
-
* @param {object} envObj - The environment object.
|
|
1460
|
-
* @memberof ServerConfBuilder
|
|
1461
|
-
*/
|
|
1462
|
-
const writeEnv = (envPath, envObj) =>
|
|
1463
|
-
fs.writeFileSync(
|
|
1464
|
-
envPath,
|
|
1465
|
-
Object.keys(envObj)
|
|
1466
|
-
.map((key) => `${key}=${envObj[key]}`)
|
|
1467
|
-
.join(`\n`),
|
|
1468
|
-
'utf8',
|
|
1469
|
-
);
|
|
1470
|
-
|
|
1471
1308
|
/**
|
|
1472
1309
|
* @method buildCliDoc
|
|
1473
1310
|
* @description Scrapes `node bin help` (and `node bin help <command>` for every
|
|
@@ -1657,35 +1494,49 @@ const getInstanceContext = async (options = { deployId, singleReplica, replicas,
|
|
|
1657
1494
|
* @param {string} options.host - The host.
|
|
1658
1495
|
* @param {string} options.path - The path.
|
|
1659
1496
|
* @param {string} options.origin - The origin.
|
|
1660
|
-
* @returns {
|
|
1497
|
+
* @returns {Promise<string|undefined>} The derived sub-conf id (`<subConf>-dev-api`), or
|
|
1498
|
+
* undefined when no client origin was given and nothing was derived.
|
|
1661
1499
|
* @memberof ServerConfBuilder
|
|
1662
1500
|
*/
|
|
1663
1501
|
const buildApiConf = async (options = { deployId: '', subConf: '', host: '', path: '', origin: '' }) => {
|
|
1664
1502
|
let { deployId, subConf, host, path, origin } = options;
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
if (process.argv[4]) host = process.argv[4].trim();
|
|
1668
|
-
if (process.argv[5]) path = process.argv[5].trim();
|
|
1503
|
+
// The client origin is what selects this mode, so it is resolved before
|
|
1504
|
+
// anything else: `src/api.js` also runs without any of these positionals.
|
|
1669
1505
|
if (process.argv[6])
|
|
1670
1506
|
origin = `${process.env.NODE_ENV === 'production' ? 'https' : 'http'}://${process.argv[6].trim()}`;
|
|
1671
|
-
|
|
1672
|
-
if (!
|
|
1507
|
+
if (!origin) return undefined;
|
|
1508
|
+
if (!deployId) deployId = process.argv[2]?.trim();
|
|
1509
|
+
if (!subConf) subConf = process.argv[3]?.trim();
|
|
1510
|
+
if (process.argv[4]) host = process.argv[4].trim();
|
|
1511
|
+
if (process.argv[5]) path = process.argv[5].trim();
|
|
1512
|
+
if (!deployId || !subConf || !host || !path) {
|
|
1513
|
+
logger.warn('Skipping api conf; a client origin needs deploy-id, sub-conf, host and path', {
|
|
1514
|
+
deployId,
|
|
1515
|
+
subConf,
|
|
1516
|
+
host,
|
|
1517
|
+
path,
|
|
1518
|
+
origin,
|
|
1519
|
+
});
|
|
1520
|
+
return undefined;
|
|
1521
|
+
}
|
|
1673
1522
|
const confServer = JSON.parse(
|
|
1674
1523
|
fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.dev.${subConf}.json`, 'utf8'),
|
|
1675
1524
|
);
|
|
1676
1525
|
const envObj = dotenv.parse(
|
|
1677
1526
|
fs.readFileSync(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}`, 'utf8'),
|
|
1678
1527
|
);
|
|
1679
|
-
if (host
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1528
|
+
if (!confServer[host]?.[path])
|
|
1529
|
+
throw new Error(`No ${host}${path} instance in conf.server.dev.${subConf}.json for ${deployId}`);
|
|
1530
|
+
confServer[host][path].origins = [origin];
|
|
1531
|
+
logger.info('Build api conf', { host, path, origin });
|
|
1532
|
+
const devApiSubConf = `${subConf}-dev-api`;
|
|
1533
|
+
writeEnv(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${devApiSubConf}`, envObj);
|
|
1684
1534
|
fs.writeFileSync(
|
|
1685
|
-
`./engine-private/conf/${deployId}/conf.server.dev.${
|
|
1535
|
+
`./engine-private/conf/${deployId}/conf.server.dev.${devApiSubConf}.json`,
|
|
1686
1536
|
JSON.stringify(confServer, null, 4),
|
|
1687
1537
|
'utf8',
|
|
1688
1538
|
);
|
|
1539
|
+
return devApiSubConf;
|
|
1689
1540
|
};
|
|
1690
1541
|
|
|
1691
1542
|
/**
|
|
@@ -1709,12 +1560,19 @@ const buildClientStaticConf = async (
|
|
|
1709
1560
|
if (!subConf) subConf = process.argv[3].trim();
|
|
1710
1561
|
if (!host) host = process.argv[4].trim();
|
|
1711
1562
|
if (!path) path = process.argv[5].trim();
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
const
|
|
1716
|
-
|
|
1717
|
-
)
|
|
1563
|
+
// The API server derives these two files, and it derives them only when it is
|
|
1564
|
+
// given the client origin. Their absence means that step has not run, which is
|
|
1565
|
+
// a setup order to state rather than an ENOENT to decode.
|
|
1566
|
+
const confPath = `./engine-private/conf/${deployId}/conf.server.dev.${subConf}-dev-api.json`;
|
|
1567
|
+
const envPath = `./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-api`;
|
|
1568
|
+
for (const source of [confPath, envPath])
|
|
1569
|
+
if (!fs.existsSync(source))
|
|
1570
|
+
throw new Error(
|
|
1571
|
+
`Missing ${source}. Start the API server first, with the client origin as its last argument: ` +
|
|
1572
|
+
`npm run dev:api ${deployId} ${subConf} ${host} ${path} <client-host:port>`,
|
|
1573
|
+
);
|
|
1574
|
+
const confServer = JSON.parse(fs.readFileSync(confPath, 'utf8'));
|
|
1575
|
+
const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
1718
1576
|
envObj.PORT = parseInt(envObj.PORT);
|
|
1719
1577
|
const apiBaseHost = devProxy
|
|
1720
1578
|
? devProxyHostFactory({ host, tls: isTlsDevProxy() })
|
|
@@ -1733,16 +1591,6 @@ const buildClientStaticConf = async (
|
|
|
1733
1591
|
);
|
|
1734
1592
|
};
|
|
1735
1593
|
|
|
1736
|
-
/**
|
|
1737
|
-
* @method isDeployRunnerContext
|
|
1738
|
-
* @description Checks if the deploy runner context is valid.
|
|
1739
|
-
* @param {string} path - The path.
|
|
1740
|
-
* @param {object} options - The options.
|
|
1741
|
-
* @returns {boolean} - The deploy runner context.
|
|
1742
|
-
* @memberof ServerConfBuilder
|
|
1743
|
-
*/
|
|
1744
|
-
const isDeployRunnerContext = (path, options) => !options.build && path && path !== 'template-deploy';
|
|
1745
|
-
|
|
1746
1594
|
/**
|
|
1747
1595
|
* @method isDevProxyContext
|
|
1748
1596
|
* @description Checks if the dev proxy context is valid.
|
|
@@ -1953,7 +1801,7 @@ const dispatchBuildInstanceEnv = ({
|
|
|
1953
1801
|
const loadProjectInstanceEnvBuilder = async (deployId) => {
|
|
1954
1802
|
const match = /^dd-([a-z0-9][a-z0-9-]*)$/.exec(`${deployId || ''}`);
|
|
1955
1803
|
if (!match) return null;
|
|
1956
|
-
const moduleUrl = new URL(
|
|
1804
|
+
const moduleUrl = new URL(`../../projects/${match[1]}/instance-data.js`, import.meta.url);
|
|
1957
1805
|
if (!fs.existsSync(moduleUrl)) return null;
|
|
1958
1806
|
const projectModule = await import(moduleUrl.href);
|
|
1959
1807
|
if (projectModule.buildInstanceEnv === undefined) return null;
|
|
@@ -2475,6 +2323,56 @@ const curlStatusChainFactory = (raw = '') => {
|
|
|
2475
2323
|
return chain;
|
|
2476
2324
|
};
|
|
2477
2325
|
|
|
2326
|
+
/**
|
|
2327
|
+
* @method publicIngressProbeFactory
|
|
2328
|
+
* @description Probes one public URL the way the traffic report does.
|
|
2329
|
+
*
|
|
2330
|
+
* `-L` follows the real redirect chain, `-v` supplies one response line per hop,
|
|
2331
|
+
* `-i` keeps full response headers available, and `-s` removes only the progress
|
|
2332
|
+
* meter. The body is discarded so a report stays bounded even when a host
|
|
2333
|
+
* returns a large application page. One implementation, because the traffic
|
|
2334
|
+
* table and the ingress event have to agree on what "reachable" means.
|
|
2335
|
+
* @param {string} url - Absolute URL to probe.
|
|
2336
|
+
* @returns {Array<string>} Ordered response codes; `['000']` when nothing answered.
|
|
2337
|
+
* @memberof ServerConfBuilder
|
|
2338
|
+
*/
|
|
2339
|
+
const publicIngressProbeFactory = (url) => {
|
|
2340
|
+
if (!url) return ['000'];
|
|
2341
|
+
const argument = shellArgumentFactory(url);
|
|
2342
|
+
return curlStatusChainFactory(
|
|
2343
|
+
shellExec(
|
|
2344
|
+
`curl -L -v -i -s --connect-timeout 3 --max-time 12 --max-redirs 10 ` +
|
|
2345
|
+
`-o /dev/null -w '\nUNDERPOST_CURL_FINAL=%{http_code}\n' ${argument} 2>&1 || true`,
|
|
2346
|
+
{ stdout: true, silent: true, silentOnError: true, disableLog: true },
|
|
2347
|
+
),
|
|
2348
|
+
);
|
|
2349
|
+
};
|
|
2350
|
+
|
|
2351
|
+
/**
|
|
2352
|
+
* @method publicIngressUrlsFactory
|
|
2353
|
+
* @description Every public URL a deploy list publishes, from the same conf the
|
|
2354
|
+
* traffic report reads.
|
|
2355
|
+
* @param {string[]} [deployList] - Deploy ids.
|
|
2356
|
+
* @param {string} [env='production'] - Environment whose routes are published.
|
|
2357
|
+
* @returns {Array<{host: string, path: string, url: string}>} Distinct routable URLs.
|
|
2358
|
+
* @memberof ServerConfBuilder
|
|
2359
|
+
*/
|
|
2360
|
+
const publicIngressUrlsFactory = (deployList = [], env = 'production') => {
|
|
2361
|
+
const urls = new Map();
|
|
2362
|
+
for (const deployId of deployList)
|
|
2363
|
+
for (const entry of deployTrafficEntriesFactory({ deployId, env }))
|
|
2364
|
+
for (const routePath of `${entry.path || '/'}`.split(' ').filter(Boolean)) {
|
|
2365
|
+
const path = routePath.startsWith('/') ? routePath : `/${routePath}`;
|
|
2366
|
+
try {
|
|
2367
|
+
const url = new URL(path, `https://${entry.host}`).href;
|
|
2368
|
+
if (!urls.has(url)) urls.set(url, { host: entry.host, path, url });
|
|
2369
|
+
} catch {
|
|
2370
|
+
/* a host the conf cannot turn into a URL is not routable */
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
return [...urls.values()];
|
|
2374
|
+
};
|
|
2375
|
+
|
|
2478
2376
|
/**
|
|
2479
2377
|
* @method trafficTableRowsFactory
|
|
2480
2378
|
* @description Resolves the live colour of each routable deployment, optionally
|
|
@@ -2557,6 +2455,25 @@ const hostRenderInstancesFactory = ({ declared = [], preserved = [], isDeployed
|
|
|
2557
2455
|
const isTrafficServingFactory = ({ liveTraffic = '', hasReadyEndpoints = () => false }) =>
|
|
2558
2456
|
!!liveTraffic && hasReadyEndpoints(liveTraffic);
|
|
2559
2457
|
|
|
2458
|
+
/**
|
|
2459
|
+
* @method redeployPlanFactory
|
|
2460
|
+
* @description The colour a redeploy targets, and how that colour is brought up.
|
|
2461
|
+
*
|
|
2462
|
+
* The inactive colour has no Deployment for most of a blue/green cycle: `run sync`
|
|
2463
|
+
* deletes it before publishing the maintenance route, and a promote leaves only
|
|
2464
|
+
* the colour it routed to. So a `rollout restart` of the target is a NotFound
|
|
2465
|
+
* error on exactly the state blue/green is meant to be in, and its absence has to
|
|
2466
|
+
* select building that colour instead of failing the deploy.
|
|
2467
|
+
* @param {string} [liveTraffic] - Colour currently routed, or empty when none is.
|
|
2468
|
+
* @param {Function} hasDeployment - `(colour) => boolean`.
|
|
2469
|
+
* @returns {{targetTraffic: string, create: boolean}} Colour to deploy, and whether it must be created rather than restarted.
|
|
2470
|
+
* @memberof ServerConfBuilder
|
|
2471
|
+
*/
|
|
2472
|
+
const redeployPlanFactory = ({ liveTraffic = '', hasDeployment = () => false }) => {
|
|
2473
|
+
const targetTraffic = nextTrafficFactory(liveTraffic);
|
|
2474
|
+
return { targetTraffic, create: !hasDeployment(targetTraffic) };
|
|
2475
|
+
};
|
|
2476
|
+
|
|
2560
2477
|
/**
|
|
2561
2478
|
* @method instanceTrafficPlanFactory
|
|
2562
2479
|
* @description Resolves, for each instance, the colour routed now and the colour
|
|
@@ -3024,32 +2941,14 @@ ${renderHosts}`,
|
|
|
3024
2941
|
return { renderHosts, changed: true };
|
|
3025
2942
|
};
|
|
3026
2943
|
|
|
3027
|
-
/**
|
|
3028
|
-
* Resolves the concrete deploy ids a build or conf-sync run should iterate over.
|
|
3029
|
-
*
|
|
3030
|
-
* The meta deploy id `dd` fans out to the comma separated ids declared in
|
|
3031
|
-
* `engine-private/deploy/dd.router`; any other value is parsed as a comma separated list.
|
|
3032
|
-
* Entries are trimmed and empties dropped.
|
|
3033
|
-
*
|
|
3034
|
-
* @method resolveDeployList
|
|
3035
|
-
* @param {string} deployId - A deploy id, a comma separated list, or the `dd` meta id.
|
|
3036
|
-
* @returns {string[]} Ordered list of concrete deploy ids.
|
|
3037
|
-
* @memberof ServerConfBuilder
|
|
3038
|
-
*/
|
|
3039
|
-
const resolveDeployList = (deployId) =>
|
|
3040
|
-
(deployId === 'dd' ? fs.readFileSync('./engine-private/deploy/dd.router', 'utf8') : deployId)
|
|
3041
|
-
.split(',')
|
|
3042
|
-
.map((id) => id.trim())
|
|
3043
|
-
.filter(Boolean);
|
|
3044
|
-
|
|
3045
2944
|
/**
|
|
3046
2945
|
* Syncs a single deploy id's private configuration into its dedicated
|
|
3047
2946
|
* `engine-<suffix>-private` repository and pushes the result.
|
|
3048
2947
|
*
|
|
3049
2948
|
* Idempotent and safe to rerun: the private repo is cloned when missing or reset to a clean
|
|
3050
|
-
* checkout when present, then the deploy id's `conf` folder
|
|
3051
|
-
* `
|
|
3052
|
-
* commit/push step is a no-op when nothing changed (`silentOnError`).
|
|
2949
|
+
* checkout when present, then the deploy id's `conf` folder (its `.env.<env>.oci` runtime
|
|
2950
|
+
* overlays included), matching `replica` entries, and any caller-supplied `extraPaths` payloads
|
|
2951
|
+
* are mirrored. The commit/push step is a no-op when nothing changed (`silentOnError`).
|
|
3053
2952
|
*
|
|
3054
2953
|
* @method syncPrivateConf
|
|
3055
2954
|
* @param {string} deployId - A concrete deploy id (e.g. `dd-cyberia`), not the `dd` meta id.
|
|
@@ -3059,10 +2958,10 @@ const resolveDeployList = (deployId) =>
|
|
|
3059
2958
|
* @memberof ServerConfBuilder
|
|
3060
2959
|
*/
|
|
3061
2960
|
const syncPrivateConf = (deployId, extraPaths = []) => {
|
|
3062
|
-
const
|
|
3063
|
-
const privateRepoName = `engine-${suffix}-private`;
|
|
2961
|
+
const privateRepoName = Underpost.repo.privateRepoFactory(deployId);
|
|
3064
2962
|
const privateGitUri = `${process.env.GITHUB_USERNAME}/${privateRepoName}`;
|
|
3065
2963
|
const privateRepoPath = `../${privateRepoName}`;
|
|
2964
|
+
const cronDeployId = cronDeployIdResolve();
|
|
3066
2965
|
|
|
3067
2966
|
if (!fs.existsSync(privateRepoPath)) {
|
|
3068
2967
|
shellExec(`cd .. && underpost clone ${privateGitUri}`, { silent: true });
|
|
@@ -3077,14 +2976,16 @@ const syncPrivateConf = (deployId, extraPaths = []) => {
|
|
|
3077
2976
|
fs.removeSync(confDest);
|
|
3078
2977
|
fs.mkdirSync(confDest, { recursive: true });
|
|
3079
2978
|
fs.copySync(`./engine-private/conf/${deployId}`, confDest);
|
|
3080
|
-
|
|
2979
|
+
// A checkout that declares no cron deploy has no `conf/<cron-id>` to mirror. Without this
|
|
2980
|
+
// the path resolves to `conf/null` and the whole sync fails over an absent optional.
|
|
2981
|
+
if (cronDeployId) fs.copySync(`./engine-private/conf/${cronDeployId}`, `${privateRepoPath}/conf/${cronDeployId}`);
|
|
2982
|
+
fs.copySync(`./engine-private/deploy`, `${privateRepoPath}/deploy`);
|
|
3081
2983
|
fs.removeSync(`${privateRepoPath}/replica`);
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
for (const entry of fs.readdirSync(
|
|
3086
|
-
if (entry.match(deployId)) fs.copySync(`${
|
|
3087
|
-
}
|
|
2984
|
+
fs.removeSync(`${privateRepoPath}/logs`);
|
|
2985
|
+
const replicaSrcDir = `./engine-private/replica`;
|
|
2986
|
+
if (fs.existsSync(replicaSrcDir))
|
|
2987
|
+
for (const entry of fs.readdirSync(replicaSrcDir))
|
|
2988
|
+
if (entry.match(deployId)) fs.copySync(`${replicaSrcDir}/${entry}`, `${privateRepoPath}/replica/${entry}`);
|
|
3088
2989
|
|
|
3089
2990
|
for (const extraPath of extraPaths) fs.copySync(`./engine-private/${extraPath}`, `${privateRepoPath}/${extraPath}`);
|
|
3090
2991
|
|
|
@@ -3117,31 +3018,111 @@ const syncDeployIdSources = (sourceMoves = []) => {
|
|
|
3117
3018
|
return true;
|
|
3118
3019
|
};
|
|
3119
3020
|
|
|
3021
|
+
const TEMPLATE_REPOSITORY_NAME = 'pwa-microservices-template';
|
|
3022
|
+
|
|
3023
|
+
/**
|
|
3024
|
+
* @method gitOriginRepositoryName
|
|
3025
|
+
* @description Repository name a checkout's `origin` points at, without the `.git`
|
|
3026
|
+
* suffix, or `null` when the path is not a work tree or declares no origin.
|
|
3027
|
+
* @param {string} repoPath - Local repository path.
|
|
3028
|
+
* @returns {string|null}
|
|
3029
|
+
* @memberof ServerConfBuilder
|
|
3030
|
+
*/
|
|
3031
|
+
const gitOriginRepositoryName = (repoPath) => {
|
|
3032
|
+
if (!fs.existsSync(`${repoPath}/.git`)) return null;
|
|
3033
|
+
const url = shellExec(`cd ${repoPath} && git config --get remote.origin.url`, {
|
|
3034
|
+
stdout: true,
|
|
3035
|
+
silent: true,
|
|
3036
|
+
disableLog: true,
|
|
3037
|
+
silentOnError: true,
|
|
3038
|
+
});
|
|
3039
|
+
const name = typeof url === 'string' ? url.trim().split('/').pop() : '';
|
|
3040
|
+
return name ? name.replace(/\.git$/, '') : null;
|
|
3041
|
+
};
|
|
3042
|
+
|
|
3043
|
+
/**
|
|
3044
|
+
* @method ensureTemplateCheckout
|
|
3045
|
+
* @description Guarantees `toPath` is a checkout of the template repository, cloning it
|
|
3046
|
+
* when it is missing or belongs to another repository.
|
|
3047
|
+
*
|
|
3048
|
+
* Publishing steps swap the checkout's `.git` for a product repository's history, so an
|
|
3049
|
+
* assembly that trusted whatever `.git` it found would read another product's identity
|
|
3050
|
+
* back into the base template `package.json`.
|
|
3051
|
+
*
|
|
3052
|
+
* @param {object} params
|
|
3053
|
+
* @param {string} params.toPath - Template checkout path.
|
|
3054
|
+
* @param {string} [params.githubUsername] - Owner of the template repository.
|
|
3055
|
+
* @param {boolean} [params.noClone=false] - Fail instead of cloning a missing/foreign checkout.
|
|
3056
|
+
* @returns {boolean} `true` when the checkout was restored.
|
|
3057
|
+
* @memberof ServerConfBuilder
|
|
3058
|
+
*/
|
|
3059
|
+
const ensureTemplateCheckout = ({ toPath, githubUsername, noClone = false }) => {
|
|
3060
|
+
const owner = githubUsername || 'underpostnet';
|
|
3061
|
+
const repository = `${owner}/${TEMPLATE_REPOSITORY_NAME}`;
|
|
3062
|
+
if (gitOriginRepositoryName(toPath) === TEMPLATE_REPOSITORY_NAME) return false;
|
|
3063
|
+
if (noClone) throw new Error(`buildTemplate: ${toPath} is not a ${repository} checkout (--no-clone)`);
|
|
3064
|
+
logger.warn('Restoring template checkout', { toPath, repository });
|
|
3065
|
+
|
|
3066
|
+
// Swap in the clone only once it is complete, so a failed one leaves the checkout as is.
|
|
3067
|
+
const templatePath = dir.resolve(toPath);
|
|
3068
|
+
const stagePath = fs.mkdtempSync(`${dir.dirname(templatePath)}/.${TEMPLATE_REPOSITORY_NAME}-`);
|
|
3069
|
+
try {
|
|
3070
|
+
shellExec(`cd ${stagePath} && node ${dir.resolve('./bin')} clone ${repository}`);
|
|
3071
|
+
const clonePath = `${stagePath}/${TEMPLATE_REPOSITORY_NAME}`;
|
|
3072
|
+
if (gitOriginRepositoryName(clonePath) !== TEMPLATE_REPOSITORY_NAME)
|
|
3073
|
+
throw new Error(`buildTemplate: could not clone ${repository}`);
|
|
3074
|
+
fs.removeSync(templatePath);
|
|
3075
|
+
fs.moveSync(clonePath, templatePath);
|
|
3076
|
+
} finally {
|
|
3077
|
+
fs.removeSync(stagePath);
|
|
3078
|
+
}
|
|
3079
|
+
return true;
|
|
3080
|
+
};
|
|
3081
|
+
|
|
3082
|
+
/**
|
|
3083
|
+
* @method pruneTemplateWorkTree
|
|
3084
|
+
* @description Empties the template checkout of everything a build reconstructs, so a
|
|
3085
|
+
* source renamed or deleted in the engine — or a client bundle belonging to another
|
|
3086
|
+
* deploy id — cannot survive from a previous run into the assembled output.
|
|
3087
|
+
* @param {string} toPath - Template checkout path.
|
|
3088
|
+
* @param {string[]} preservedEntries - Top level entries to keep (git history, installed modules).
|
|
3089
|
+
* @returns {void}
|
|
3090
|
+
* @memberof ServerConfBuilder
|
|
3091
|
+
*/
|
|
3092
|
+
const pruneTemplateWorkTree = (toPath, preservedEntries = []) => {
|
|
3093
|
+
for (const entry of fs.readdirSync(toPath)) {
|
|
3094
|
+
if (preservedEntries.includes(entry)) continue;
|
|
3095
|
+
fs.removeSync(`${toPath}/${entry}`);
|
|
3096
|
+
}
|
|
3097
|
+
};
|
|
3098
|
+
|
|
3120
3099
|
/**
|
|
3121
3100
|
* Rebuilds the standalone `pwa-microservices-template` from scratch out of the current
|
|
3122
3101
|
* engine source tree.
|
|
3123
3102
|
*
|
|
3124
|
-
* Clones the template repo next to the engine when
|
|
3125
|
-
*
|
|
3103
|
+
* Clones the template repo next to the engine when the checkout is missing or belongs to
|
|
3104
|
+
* another repository, empties it so the result cannot inherit anything from a previous run,
|
|
3105
|
+
* then syncs every engine-tracked file the template is allowed to carry
|
|
3126
3106
|
* ({@link validateTemplatePath}), strips engine-only + product modules, restores the template's
|
|
3127
3107
|
* own CI workflows + guest services, and rewrites `package.json` / `package-lock.json` / `README`
|
|
3128
3108
|
* so the result is a standalone, installable project. Throws on failure; callers own exit codes.
|
|
3129
3109
|
*
|
|
3130
|
-
* Product catalogs are read dynamically ({@link module:src/server/catalog} `loadProductCatalogs`),
|
|
3110
|
+
* Product catalogs are read dynamically ({@link module:src/server/build/catalog.js} `loadProductCatalogs`),
|
|
3131
3111
|
* so this stays decoupled from — and survives removal of — any product module.
|
|
3132
3112
|
*
|
|
3133
3113
|
* @method buildTemplate
|
|
3134
3114
|
* @param {object} [options]
|
|
3135
3115
|
* @param {string} [options.srcPath='./'] - Engine source root to sync from.
|
|
3136
3116
|
* @param {string} [options.toPath='../pwa-microservices-template'] - Template output path.
|
|
3117
|
+
* @param {boolean} [options.noClone=false] - Fail instead of cloning when the checkout is missing or foreign.
|
|
3137
3118
|
* @returns {Promise<void>}
|
|
3138
3119
|
* @memberof ServerConfBuilder
|
|
3139
3120
|
*/
|
|
3140
|
-
const buildTemplate = async ({ srcPath = './', toPath = '../pwa-microservices-template' } = {}) => {
|
|
3121
|
+
const buildTemplate = async ({ srcPath = './', toPath = '../pwa-microservices-template', noClone = false } = {}) => {
|
|
3141
3122
|
const walk = (await import('ignore-walk')).default;
|
|
3142
|
-
const { TEMPLATE_RESTORE_PATHS, TEMPLATE_KEYWORDS, TEMPLATE_DESCRIPTION } =
|
|
3143
|
-
await import('
|
|
3144
|
-
const { loadProductCatalogs } = await import('
|
|
3123
|
+
const { TEMPLATE_RESTORE_PATHS, TEMPLATE_PRESERVED_ENTRIES, TEMPLATE_KEYWORDS, TEMPLATE_DESCRIPTION } =
|
|
3124
|
+
await import('../../projects/underpost/catalog-underpost.js');
|
|
3125
|
+
const { loadProductCatalogs } = await import('../build/catalog.js');
|
|
3145
3126
|
const githubUsername = process.env.GITHUB_USERNAME;
|
|
3146
3127
|
|
|
3147
3128
|
logger.info('Build template', { srcPath, toPath });
|
|
@@ -3154,10 +3135,9 @@ const buildTemplate = async ({ srcPath = './', toPath = '../pwa-microservices-te
|
|
|
3154
3135
|
)
|
|
3155
3136
|
).filter((p) => !p.startsWith('.git'));
|
|
3156
3137
|
|
|
3157
|
-
|
|
3158
|
-
shellExec(`cd .. && node engine/bin clone ${githubUsername}/pwa-microservices-template`);
|
|
3159
|
-
|
|
3138
|
+
ensureTemplateCheckout({ toPath, githubUsername, noClone });
|
|
3160
3139
|
shellExec(`cd ${toPath} && git config core.filemode false`);
|
|
3140
|
+
pruneTemplateWorkTree(toPath, TEMPLATE_PRESERVED_ENTRIES);
|
|
3161
3141
|
|
|
3162
3142
|
for (const copyPath of sourceFiles) {
|
|
3163
3143
|
if (copyPath === 'NaN') continue;
|
|
@@ -3186,6 +3166,7 @@ const buildTemplate = async ({ srcPath = './', toPath = '../pwa-microservices-te
|
|
|
3186
3166
|
}
|
|
3187
3167
|
shellExec(`rm -rf ${toPath}/.github`);
|
|
3188
3168
|
shellExec(`rm -rf ${toPath}/manifests/deployment/dd-*`);
|
|
3169
|
+
shellExec(`rm -rf ${toPath}/deploy`);
|
|
3189
3170
|
|
|
3190
3171
|
fs.mkdirSync(`${toPath}/.github/workflows`, { recursive: true });
|
|
3191
3172
|
for (const restorePath of TEMPLATE_RESTORE_PATHS) {
|
|
@@ -3265,7 +3246,8 @@ git add .`);
|
|
|
3265
3246
|
* `engine-<idPart>`). A pod started with `underpost start --build --private-test-repo`
|
|
3266
3247
|
* clones this repo, so work-in-progress engine source can be tested end to end
|
|
3267
3248
|
* without touching the production source. Mirrors {@link updatePrivateTemplateRepo}
|
|
3268
|
-
* but per-deploy-id and against the test repo
|
|
3249
|
+
* but per-deploy-id and against the test repo, publishing from its own work tree
|
|
3250
|
+
* (`<template parent>/engine-test-<idPart>`) so the template checkout keeps its identity.
|
|
3269
3251
|
*
|
|
3270
3252
|
* Assumes the deploy id template has already been assembled at the template path
|
|
3271
3253
|
* (run `node bin/build <deployId>` first, or use `node bin/build <deployId> --update-private`).
|
|
@@ -3275,39 +3257,53 @@ git add .`);
|
|
|
3275
3257
|
*/
|
|
3276
3258
|
const updatePrivateEngineTestRepo = async (deployId) => {
|
|
3277
3259
|
const username = process.env.GITHUB_USERNAME || 'underpostnet';
|
|
3278
|
-
const repoName =
|
|
3260
|
+
const repoName = Underpost.repo.engineRepoFactory(deployId, { test: true });
|
|
3279
3261
|
const templatePath = '/home/dd/pwa-microservices-template';
|
|
3280
3262
|
if (!fs.existsSync(templatePath))
|
|
3281
3263
|
throw new Error(`updatePrivateEngineTestRepo: assemble the template first (node bin/build ${deployId})`);
|
|
3282
3264
|
|
|
3283
|
-
// Detach the assembled working tree from any engine-build git history.
|
|
3284
|
-
shellExec(`sudo rm -rf ${templatePath}/.git`);
|
|
3285
|
-
|
|
3286
3265
|
// Adopt the test repo's existing history when present (so the push is a delta);
|
|
3287
3266
|
// otherwise publish a fresh history on first push.
|
|
3288
|
-
|
|
3267
|
+
const parentPath = dir.dirname(templatePath);
|
|
3268
|
+
shellExec(`cd ${parentPath} && sudo rm -rf ./${repoName}.git && underpost clone --bare ${username}/${repoName}`, {
|
|
3289
3269
|
silent: true,
|
|
3290
3270
|
disableLog: true,
|
|
3291
3271
|
silentOnError: true,
|
|
3292
3272
|
});
|
|
3293
|
-
|
|
3273
|
+
|
|
3274
|
+
// Publishing from a work tree of its own leaves the template checkout its own git
|
|
3275
|
+
// history, and copying that tree from 0 keeps a stale file out of the published one.
|
|
3276
|
+
const publishPath = `${parentPath}/${repoName}`;
|
|
3277
|
+
shellExec(`sudo rm -rf ${publishPath}`);
|
|
3278
|
+
fs.copySync(templatePath, publishPath, {
|
|
3279
|
+
filter: (src) => {
|
|
3280
|
+
const entries = dir.relative(templatePath, src).split(dir.sep);
|
|
3281
|
+
return !entries.includes('.git') && !entries.includes('node_modules');
|
|
3282
|
+
},
|
|
3283
|
+
});
|
|
3284
|
+
if (fs.existsSync(`${parentPath}/${repoName}.git`)) shellExec(`mv ${parentPath}/${repoName}.git ${publishPath}/.git`);
|
|
3294
3285
|
|
|
3295
3286
|
// `git init` converts the moved bare repo into a normal work-tree repo (bare
|
|
3296
3287
|
// clones have no work tree, so `git add` would fail), and bootstraps a fresh
|
|
3297
3288
|
// repo on first publish. Idempotent — mirrors updatePrivateTemplateRepo.
|
|
3298
|
-
shellExec(`cd ${
|
|
3289
|
+
shellExec(`cd ${publishPath}
|
|
3299
3290
|
git init
|
|
3300
3291
|
git config user.name '${username}'
|
|
3301
3292
|
git config user.email 'development@underpost.net'
|
|
3302
|
-
git add .`);
|
|
3293
|
+
git add -A .`);
|
|
3303
3294
|
|
|
3304
|
-
|
|
3295
|
+
// The bundled coverage report is a build output the artifact has to carry: force-added so a
|
|
3296
|
+
// tree-level ignore rule can never silently drop it from the published source.
|
|
3297
|
+
if (fs.existsSync(`${publishPath}/${COVERAGE_BUNDLE_DIRECTORY}`))
|
|
3298
|
+
shellExec(`cd ${publishPath} && git add -A -f ${COVERAGE_BUNDLE_DIRECTORY}`);
|
|
3299
|
+
|
|
3300
|
+
const hasChanges = shellExec(`node bin cmt ${publishPath} --has-changes`, {
|
|
3305
3301
|
stdout: true,
|
|
3306
3302
|
silent: true,
|
|
3307
3303
|
disableLog: true,
|
|
3308
3304
|
}).trim();
|
|
3309
3305
|
if (hasChanges === '1')
|
|
3310
|
-
shellExec(`cd ${
|
|
3306
|
+
shellExec(`cd ${publishPath} && git commit -m 'Update ${repoName}' && underpost push . ${username}/${repoName}`);
|
|
3311
3307
|
else logger.info('No changes to publish', { repoName });
|
|
3312
3308
|
};
|
|
3313
3309
|
|
|
@@ -3328,12 +3324,15 @@ git add .`);
|
|
|
3328
3324
|
* instance runs — `/etc/hosts` is written in one pass for every host the
|
|
3329
3325
|
* gateway will serve.
|
|
3330
3326
|
* @param {Array<string>} deployList - Deploy ids being brought up.
|
|
3331
|
-
* @param {string} [instanceList] -
|
|
3327
|
+
* @param {string} [instanceList] - Comma-separated instance/template ids, as `--instance-id` takes them.
|
|
3332
3328
|
* @returns {{ byDeployId: Object<string,{ids: Array<string>, hosts: Array<string>}>, unmatched: Array<string> }}
|
|
3333
3329
|
* Per-deploy selection, and the requested ids no deploy declares.
|
|
3334
3330
|
*/
|
|
3335
3331
|
const clusterInstancesFactory = (deployList = [], instanceList = '') => {
|
|
3336
|
-
const requested = `${instanceList || ''}
|
|
3332
|
+
const requested = `${instanceList || ''}`
|
|
3333
|
+
.split(',')
|
|
3334
|
+
.map((id) => id.trim())
|
|
3335
|
+
.filter(Boolean);
|
|
3337
3336
|
const byDeployId = Object.fromEntries(
|
|
3338
3337
|
deployList.map((deployId) => {
|
|
3339
3338
|
const confPath = `./engine-private/conf/${deployId}/conf.instances.json`;
|
|
@@ -3358,6 +3357,10 @@ const clusterInstancesFactory = (deployList = [], instanceList = '') => {
|
|
|
3358
3357
|
};
|
|
3359
3358
|
|
|
3360
3359
|
export {
|
|
3360
|
+
cleanDeployEnvFiles,
|
|
3361
|
+
deployEnvFilePath,
|
|
3362
|
+
deployEnvNamesFactory,
|
|
3363
|
+
instanceEnvFilePath,
|
|
3361
3364
|
Config,
|
|
3362
3365
|
loadConf,
|
|
3363
3366
|
loadConfInstances,
|
|
@@ -3377,28 +3380,19 @@ export {
|
|
|
3377
3380
|
addWsConf,
|
|
3378
3381
|
buildWsSrc,
|
|
3379
3382
|
cloneSrcComponents,
|
|
3380
|
-
buildProxyRouter,
|
|
3381
3383
|
getDataDeploy,
|
|
3382
3384
|
validateTemplatePath,
|
|
3383
3385
|
buildReplicaId,
|
|
3384
3386
|
mergeFile,
|
|
3385
3387
|
getPathsSSR,
|
|
3386
|
-
buildKindPorts,
|
|
3387
|
-
buildPortProxyRouter,
|
|
3388
3388
|
splitFileFactory,
|
|
3389
3389
|
generateSecurePassword,
|
|
3390
3390
|
resolveReplicaCount,
|
|
3391
|
-
getNpmRootPath,
|
|
3392
|
-
getUnderpostRootPath,
|
|
3393
|
-
writeEnv,
|
|
3394
|
-
pathPortAssignmentFactory,
|
|
3395
|
-
deployRangePortFactory,
|
|
3396
3391
|
awaitDeployMonitor,
|
|
3397
3392
|
buildCliDoc,
|
|
3398
3393
|
getInstanceContext,
|
|
3399
3394
|
buildApiConf,
|
|
3400
3395
|
buildClientStaticConf,
|
|
3401
|
-
isDeployRunnerContext,
|
|
3402
3396
|
isDevProxyContext,
|
|
3403
3397
|
devProxyHostFactory,
|
|
3404
3398
|
isTlsDevProxy,
|
|
@@ -3408,10 +3402,12 @@ export {
|
|
|
3408
3402
|
loadConfServerJson,
|
|
3409
3403
|
getConfFolder,
|
|
3410
3404
|
getConfFilePath,
|
|
3405
|
+
deployEnvContentFactory,
|
|
3406
|
+
deployOciEnvFilePath,
|
|
3407
|
+
ociEnvContentFactory,
|
|
3408
|
+
OCI_ENV_SUFFIX,
|
|
3411
3409
|
readConfJson,
|
|
3412
3410
|
DEFAULT_DEPLOY_ID,
|
|
3413
|
-
loadCronDeployEnv,
|
|
3414
|
-
cronDeployIdResolve,
|
|
3415
3411
|
clusterContextFactory,
|
|
3416
3412
|
clusterTypeFactory,
|
|
3417
3413
|
exposeTcpPortsFactory,
|
|
@@ -3434,10 +3430,13 @@ export {
|
|
|
3434
3430
|
trafficProbePathsFactory,
|
|
3435
3431
|
hostIngressFactsFactory,
|
|
3436
3432
|
curlStatusChainFactory,
|
|
3433
|
+
publicIngressProbeFactory,
|
|
3434
|
+
publicIngressUrlsFactory,
|
|
3437
3435
|
hostRenderInstancesFactory,
|
|
3438
3436
|
instanceTrafficPlanFactory,
|
|
3439
3437
|
isTrafficServingFactory,
|
|
3440
3438
|
nextTrafficFactory,
|
|
3439
|
+
redeployPlanFactory,
|
|
3441
3440
|
schedulableNodeFactory,
|
|
3442
3441
|
stopPlanFactory,
|
|
3443
3442
|
trafficFromRoutingInfoFactory,
|
|
@@ -3445,9 +3444,11 @@ export {
|
|
|
3445
3444
|
resolveEnvScoped,
|
|
3446
3445
|
sortInstancesByPath,
|
|
3447
3446
|
waitForPort,
|
|
3448
|
-
resolveDeployList,
|
|
3449
3447
|
syncPrivateConf,
|
|
3450
3448
|
syncDeployIdSources,
|
|
3449
|
+
gitOriginRepositoryName,
|
|
3450
|
+
ensureTemplateCheckout,
|
|
3451
|
+
pruneTemplateWorkTree,
|
|
3451
3452
|
buildTemplate,
|
|
3452
3453
|
updatePrivateTemplateRepo,
|
|
3453
3454
|
updatePrivateEngineTestRepo,
|