cyberia 3.1.3 → 3.2.9
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/.env.example +0 -2
- package/.github/workflows/engine-cyberia.cd.yml +10 -8
- package/.github/workflows/engine-cyberia.ci.yml +12 -29
- package/.github/workflows/ghpkg.ci.yml +4 -4
- package/.github/workflows/npmpkg.ci.yml +28 -11
- package/.github/workflows/publish.ci.yml +21 -2
- package/.github/workflows/pwa-microservices-template-page.cd.yml +4 -5
- package/.github/workflows/pwa-microservices-template-test.ci.yml +3 -3
- package/.github/workflows/release.cd.yml +14 -10
- package/CHANGELOG.md +783 -1
- package/CLI-HELP.md +95 -18
- package/Dockerfile +0 -2
- package/README.md +290 -220
- package/bin/build.js +24 -7
- package/bin/cyberia.js +2838 -252
- package/bin/deploy.js +747 -125
- package/bin/file.js +9 -0
- package/bin/index.js +2838 -252
- package/bin/vs.js +1 -1
- package/conf.js +99 -65
- package/deployment.yaml +18 -164
- package/hardhat/hardhat.config.js +13 -13
- package/hardhat/ignition/modules/ObjectLayerToken.js +1 -1
- package/hardhat/package-lock.json +2559 -5864
- package/hardhat/package.json +14 -23
- package/hardhat/scripts/deployObjectLayerToken.js +1 -1
- package/hardhat/test/ObjectLayerToken.js +4 -2
- package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
- package/hardhat/types/ethers-contracts/common.ts +92 -0
- package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
- package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
- package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
- package/hardhat/types/ethers-contracts/index.ts +6 -0
- package/jsconfig.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +6 -5
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +6 -5
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +18 -164
- package/manifests/deployment/dd-cyberia-development/proxy.yaml +7 -79
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
- package/manifests/deployment/dd-test-development/deployment.yaml +112 -28
- package/manifests/deployment/dd-test-development/proxy.yaml +46 -1
- package/manifests/deployment/playwright/deployment.yaml +1 -1
- package/nodemon.json +1 -1
- package/package.json +39 -24
- package/proxy.yaml +7 -79
- package/scripts/k3s-node-setup.sh +2 -2
- package/scripts/nat-iptables.sh +103 -18
- package/scripts/rhel-grpc-setup.sh +56 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +58 -14
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +23 -14
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +148 -20
- package/src/api/core/core.controller.js +10 -10
- package/src/api/core/core.service.js +10 -10
- package/src/api/crypto/crypto.controller.js +8 -8
- package/src/api/crypto/crypto.service.js +8 -8
- package/src/api/cyberia-action/cyberia-action.controller.js +74 -0
- package/src/api/cyberia-action/cyberia-action.model.js +87 -0
- package/src/api/cyberia-action/cyberia-action.router.js +27 -0
- package/src/api/cyberia-action/cyberia-action.service.js +42 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
- package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
- package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
- package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
- package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +178 -0
- package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
- package/src/api/cyberia-instance/cyberia-instance.model.js +87 -0
- package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
- package/src/api/cyberia-instance/cyberia-instance.service.js +156 -0
- package/src/api/cyberia-instance/cyberia-portal-connector.js +260 -0
- package/src/api/cyberia-instance/cyberia-world-generator.js +505 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +574 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +231 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +46 -0
- package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
- package/src/api/cyberia-map/cyberia-map.model.js +30 -0
- package/src/api/cyberia-map/cyberia-map.router.js +40 -0
- package/src/api/cyberia-map/cyberia-map.service.js +74 -0
- package/src/api/cyberia-quest/cyberia-quest.controller.js +74 -0
- package/src/api/cyberia-quest/cyberia-quest.model.js +67 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +27 -0
- package/src/api/cyberia-quest/cyberia-quest.service.js +42 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +74 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +49 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +27 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +42 -0
- package/src/api/default/default.controller.js +10 -10
- package/src/api/default/default.service.js +10 -10
- package/src/api/document/document.controller.js +12 -12
- package/src/api/document/document.model.js +10 -16
- package/src/api/file/file.controller.js +8 -8
- package/src/api/file/file.model.js +10 -10
- package/src/api/file/file.ref.json +18 -0
- package/src/api/file/file.service.js +36 -36
- package/src/api/instance/instance.controller.js +10 -10
- package/src/api/instance/instance.model.js +4 -10
- package/src/api/instance/instance.service.js +10 -10
- package/src/api/ipfs/ipfs.controller.js +15 -36
- package/src/api/ipfs/ipfs.model.js +47 -47
- package/src/api/ipfs/ipfs.router.js +8 -13
- package/src/api/ipfs/ipfs.service.js +67 -129
- package/src/api/object-layer/object-layer.controller.js +12 -12
- package/src/api/object-layer/object-layer.model.js +4 -17
- package/src/api/object-layer/object-layer.router.js +30 -0
- package/src/api/object-layer/object-layer.service.js +126 -43
- package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +10 -10
- package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +6 -16
- package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +18 -14
- package/src/api/test/test.controller.js +8 -8
- package/src/api/test/test.service.js +8 -8
- package/src/api/user/guest.service.js +99 -0
- package/src/api/user/user.controller.js +6 -6
- package/src/api/user/user.model.js +8 -13
- package/src/api/user/user.service.js +11 -27
- package/src/cli/cluster.js +68 -21
- package/src/cli/db.js +753 -825
- package/src/cli/deploy.js +215 -125
- package/src/cli/env.js +29 -0
- package/src/cli/fs.js +82 -8
- package/src/cli/image.js +43 -1
- package/src/cli/index.js +74 -3
- package/src/cli/kubectl.js +211 -0
- package/src/cli/release.js +340 -0
- package/src/cli/repository.js +475 -74
- package/src/cli/run.js +582 -43
- package/src/cli/secrets.js +73 -0
- package/src/cli/ssh.js +1 -1
- package/src/cli/static.js +43 -115
- package/src/cli/test.js +3 -3
- package/src/client/Cryptokoyn.index.js +18 -22
- package/src/client/CyberiaPortal.index.js +19 -24
- package/src/client/Default.index.js +21 -34
- package/src/client/Itemledger.index.js +20 -27
- package/src/client/Underpost.index.js +19 -24
- package/src/client/components/core/404.js +4 -4
- package/src/client/components/core/500.js +4 -4
- package/src/client/components/core/Account.js +73 -60
- package/src/client/components/core/AgGrid.js +23 -33
- package/src/client/components/core/Alert.js +12 -13
- package/src/client/components/core/AppStore.js +69 -0
- package/src/client/components/core/Auth.js +35 -37
- package/src/client/components/core/Badge.js +7 -13
- package/src/client/components/core/BtnIcon.js +15 -17
- package/src/client/components/core/CalendarCore.js +43 -64
- package/src/client/components/core/Chat.js +13 -15
- package/src/client/components/core/ClientEvents.js +87 -0
- package/src/client/components/core/ColorPaletteElement.js +309 -0
- package/src/client/components/core/Content.js +17 -14
- package/src/client/components/core/Css.js +15 -71
- package/src/client/components/core/CssCore.js +12 -16
- package/src/client/components/core/D3Chart.js +4 -4
- package/src/client/components/core/Docs.js +64 -91
- package/src/client/components/core/DropDown.js +194 -96
- package/src/client/components/core/EventBus.js +92 -0
- package/src/client/components/core/EventsUI.js +14 -17
- package/src/client/components/core/FileExplorer.js +96 -228
- package/src/client/components/core/FullScreen.js +47 -75
- package/src/client/components/core/Input.js +24 -69
- package/src/client/components/core/Keyboard.js +26 -19
- package/src/client/components/core/KeyboardAvoidance.js +145 -0
- package/src/client/components/core/LoadingAnimation.js +25 -31
- package/src/client/components/core/LogIn.js +43 -43
- package/src/client/components/core/LogOut.js +25 -16
- package/src/client/components/core/Modal.js +462 -179
- package/src/client/components/core/NotificationManager.js +14 -18
- package/src/client/components/core/Panel.js +54 -51
- package/src/client/components/core/PanelForm.js +44 -144
- package/src/client/components/core/Polyhedron.js +110 -214
- package/src/client/components/core/PublicProfile.js +39 -32
- package/src/client/components/core/Recover.js +48 -44
- package/src/client/components/core/Responsive.js +88 -32
- package/src/client/components/core/RichText.js +9 -18
- package/src/client/components/core/Router.js +24 -3
- package/src/client/components/core/SearchBox.js +37 -37
- package/src/client/components/core/SignUp.js +39 -30
- package/src/client/components/core/SocketIo.js +112 -30
- package/src/client/components/core/SocketIoHandler.js +75 -0
- package/src/client/components/core/Stream.js +143 -95
- package/src/client/components/core/ToggleSwitch.js +8 -20
- package/src/client/components/core/ToolTip.js +5 -17
- package/src/client/components/core/Translate.js +56 -59
- package/src/client/components/core/Validator.js +26 -16
- package/src/client/components/core/Wallet.js +15 -26
- package/src/client/components/core/Webhook.js +40 -7
- package/src/client/components/core/Worker.js +163 -27
- package/src/client/components/core/windowGetDimensions.js +7 -7
- package/src/client/components/cryptokoyn/{MenuCryptokoyn.js → AppShellCryptokoyn.js} +59 -59
- package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
- package/src/client/components/cryptokoyn/CssCryptokoyn.js +15 -15
- package/src/client/components/cryptokoyn/LogInCryptokoyn.js +9 -7
- package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +8 -6
- package/src/client/components/cryptokoyn/RouterCryptokoyn.js +37 -0
- package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +4 -4
- package/src/client/components/cryptokoyn/SignUpCryptokoyn.js +6 -4
- package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
- package/src/client/components/cyberia/InstanceEngineCyberia.js +781 -0
- package/src/client/components/cyberia/MapEngineCyberia.js +1836 -2
- package/src/client/components/cyberia/ObjectLayerEngine.js +19 -0
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +1220 -99
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +252 -316
- package/src/client/components/cyberia-portal/{MenuCyberiaPortal.js → AppShellCyberiaPortal.js} +136 -103
- package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
- package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +462 -32
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +15 -15
- package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +9 -7
- package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +8 -6
- package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +4 -4
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +60 -0
- package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +4 -4
- package/src/client/components/cyberia-portal/SignUpCyberiaPortal.js +6 -4
- package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +8 -4
- package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +91 -91
- package/src/client/components/default/AppStoreDefault.js +5 -0
- package/src/client/components/default/CssDefault.js +12 -12
- package/src/client/components/default/LogInDefault.js +9 -7
- package/src/client/components/default/LogOutDefault.js +8 -6
- package/src/client/components/default/RouterDefault.js +47 -0
- package/src/client/components/default/SettingsDefault.js +4 -4
- package/src/client/components/default/SignUpDefault.js +6 -4
- package/src/client/components/default/SocketIoDefault.js +3 -51
- package/src/client/components/default/TranslateDefault.js +3 -3
- package/src/client/components/itemledger/{MenuItemledger.js → AppShellItemledger.js} +59 -59
- package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
- package/src/client/components/itemledger/CssItemledger.js +15 -15
- package/src/client/components/itemledger/LogInItemledger.js +9 -7
- package/src/client/components/itemledger/LogOutItemledger.js +8 -6
- package/src/client/components/itemledger/RouterItemledger.js +38 -0
- package/src/client/components/itemledger/SettingsItemledger.js +4 -4
- package/src/client/components/itemledger/SignUpItemledger.js +6 -4
- package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
- package/src/client/components/itemledger/TranslateItemledger.js +3 -3
- package/src/client/components/underpost/{MenuUnderpost.js → AppShellUnderpost.js} +92 -92
- package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
- package/src/client/components/underpost/CssUnderpost.js +14 -14
- package/src/client/components/underpost/CyberpunkBloggerUnderpost.js +4 -4
- package/src/client/components/underpost/DocumentSearchProvider.js +1 -1
- package/src/client/components/underpost/LabGalleryUnderpost.js +12 -15
- package/src/client/components/underpost/LogInUnderpost.js +9 -7
- package/src/client/components/underpost/LogOutUnderpost.js +8 -6
- package/src/client/components/underpost/RouterUnderpost.js +45 -0
- package/src/client/components/underpost/SettingsUnderpost.js +4 -4
- package/src/client/components/underpost/SignUpUnderpost.js +6 -4
- package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
- package/src/client/components/underpost/TranslateUnderpost.js +4 -4
- package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +235 -0
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +443 -0
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +417 -0
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +313 -0
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +260 -0
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +241 -0
- package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +300 -0
- package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +279 -0
- package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +206 -0
- package/src/client/public/cyberia-docs/ROADMAP.md +240 -0
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +732 -0
- package/src/client/services/atlas-sprite-sheet/atlas-sprite-sheet.service.js +14 -20
- package/src/client/services/core/core.service.js +35 -55
- package/src/client/services/crypto/crypto.service.js +8 -13
- package/src/client/services/cyberia-action/cyberia-action.service.js +99 -0
- package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +99 -0
- package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
- package/src/client/services/cyberia-entity/cyberia-entity.service.js +99 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +116 -0
- package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +99 -0
- package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
- package/src/client/services/cyberia-map/cyberia-map.service.js +120 -0
- package/src/client/services/cyberia-quest/cyberia-quest.service.js +99 -0
- package/src/client/services/cyberia-quest-progress/cyberia-quest-progress.service.js +99 -0
- package/src/client/services/default/default.management.js +159 -267
- package/src/client/services/default/default.service.js +10 -16
- package/src/client/services/document/document.service.js +14 -19
- package/src/client/services/file/file.service.js +8 -13
- package/src/client/services/instance/instance.management.js +6 -6
- package/src/client/services/instance/instance.service.js +10 -15
- package/src/client/services/ipfs/ipfs.service.js +14 -40
- package/src/client/services/object-layer/object-layer.management.js +14 -14
- package/src/client/services/object-layer/object-layer.service.js +39 -24
- package/src/client/services/object-layer-render-frames/object-layer-render-frames.service.js +10 -16
- package/src/client/services/test/test.service.js +8 -13
- package/src/client/services/user/guest.service.js +86 -0
- package/src/client/services/user/user.management.js +6 -6
- package/src/client/services/user/user.service.js +14 -20
- package/src/client/ssr/body/404.js +3 -3
- package/src/client/ssr/body/500.js +3 -3
- package/src/client/ssr/body/CacheControl.js +5 -2
- package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +13 -6
- package/src/client/ssr/head/PwaItemledger.js +197 -60
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
- package/src/client/ssr/offline/Maintenance.js +12 -11
- package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
- package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
- package/src/client/ssr/pages/Test.js +2 -2
- package/src/client/sw/core.sw.js +212 -0
- package/src/grpc/cyberia/grpc-server.js +642 -0
- package/src/index.js +24 -1
- package/src/runtime/cyberia-client/Dockerfile +80 -0
- package/src/runtime/cyberia-server/Dockerfile +37 -0
- package/src/runtime/express/Dockerfile +5 -1
- package/src/runtime/express/Express.js +18 -1
- package/src/runtime/lampp/Dockerfile +17 -5
- package/src/runtime/lampp/Lampp.js +27 -4
- package/src/runtime/wp/Dockerfile +62 -0
- package/src/runtime/wp/Wp.js +639 -0
- package/src/server/atlas-sprite-sheet-generator.js +4 -2
- package/src/server/auth.js +24 -1
- package/src/server/backup.js +37 -9
- package/src/server/client-build-docs.js +52 -46
- package/src/server/client-build.js +356 -82
- package/src/server/client-formatted.js +140 -57
- package/src/server/conf.js +29 -13
- package/src/server/cron.js +25 -23
- package/src/server/data-query.js +32 -20
- package/src/server/dns.js +24 -1
- package/src/server/ipfs-client.js +253 -89
- package/src/server/object-layer.js +150 -114
- package/src/server/peer.js +8 -0
- package/src/server/process.js +13 -27
- package/src/server/runtime.js +25 -1
- package/src/server/semantic-layer-generator-floor.js +319 -0
- package/src/server/semantic-layer-generator-resource.js +259 -0
- package/src/server/semantic-layer-generator-skin.js +1164 -0
- package/src/server/semantic-layer-generator.js +211 -542
- package/src/server/shape-generator.js +108 -0
- package/src/server/start.js +19 -5
- package/src/server/valkey.js +141 -235
- package/src/ws/IoInterface.js +1 -10
- package/src/ws/IoServer.js +14 -33
- package/src/ws/core/channels/core.ws.chat.js +65 -20
- package/src/ws/core/channels/core.ws.mailer.js +113 -32
- package/src/ws/core/channels/core.ws.stream.js +90 -31
- package/src/ws/core/core.ws.connection.js +12 -33
- package/src/ws/core/core.ws.emit.js +10 -26
- package/src/ws/core/core.ws.server.js +25 -58
- package/src/ws/default/channels/default.ws.main.js +53 -12
- package/src/ws/default/default.ws.connection.js +26 -13
- package/src/ws/default/default.ws.server.js +30 -12
- package/tsconfig.docs.json +15 -0
- package/typedoc.dd-cyberia.json +29 -0
- package/typedoc.json +29 -0
- package/WHITE-PAPER.md +0 -1540
- package/hardhat/README.md +0 -531
- package/hardhat/WHITE-PAPER.md +0 -1540
- package/jsdoc.dd-cyberia.json +0 -59
- package/jsdoc.json +0 -59
- package/src/api/object-layer/README.md +0 -347
- package/src/client/components/core/ColorPalette.js +0 -5267
- package/src/client/components/core/JoyStick.js +0 -80
- package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
- package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
- package/src/client/components/cryptokoyn/RoutesCryptokoyn.js +0 -39
- package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
- package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +0 -58
- package/src/client/components/cyberia-portal/ServerCyberiaPortal.js +0 -136
- package/src/client/components/default/ElementsDefault.js +0 -38
- package/src/client/components/default/RoutesDefault.js +0 -49
- package/src/client/components/itemledger/CommonItemledger.js +0 -29
- package/src/client/components/itemledger/ElementsItemledger.js +0 -38
- package/src/client/components/itemledger/RoutesItemledger.js +0 -40
- package/src/client/components/underpost/CommonUnderpost.js +0 -29
- package/src/client/components/underpost/ElementsUnderpost.js +0 -38
- package/src/client/components/underpost/RoutesUnderpost.js +0 -47
- package/src/client/sw/default.sw.js +0 -127
- package/src/client/sw/template.sw.js +0 -84
- package/src/ws/core/management/core.ws.chat.js +0 -8
- package/src/ws/core/management/core.ws.mailer.js +0 -16
- package/src/ws/core/management/core.ws.stream.js +0 -8
- package/src/ws/default/management/default.ws.main.js +0 -8
package/src/cli/run.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* @namespace UnderpostRun
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { daemonProcess, getTerminalPid, shellCd, shellExec } from '../server/process.js';
|
|
7
|
+
import { daemonProcess, getTerminalPid, pbcopy, shellCd, shellExec } from '../server/process.js';
|
|
8
|
+
import crypto from 'crypto';
|
|
8
9
|
import {
|
|
9
10
|
awaitDeployMonitor,
|
|
10
11
|
buildKindPorts,
|
|
@@ -17,12 +18,31 @@ import {
|
|
|
17
18
|
import { actionInitLog, loggerFactory } from '../server/logger.js';
|
|
18
19
|
|
|
19
20
|
import fs from 'fs-extra';
|
|
21
|
+
import net from 'net';
|
|
20
22
|
import { range, setPad, timer } from '../client/components/core/CommonJs.js';
|
|
21
23
|
|
|
22
24
|
import os from 'os';
|
|
23
25
|
import Underpost from '../index.js';
|
|
24
26
|
import dotenv from 'dotenv';
|
|
25
27
|
|
|
28
|
+
const waitForPort = (port, host = '127.0.0.1', { maxAttempts = 30, interval = 2000 } = {}) =>
|
|
29
|
+
new Promise((resolve, reject) => {
|
|
30
|
+
let attempts = 0;
|
|
31
|
+
const tryConnect = () => {
|
|
32
|
+
attempts++;
|
|
33
|
+
const socket = net.createConnection({ port, host }, () => {
|
|
34
|
+
socket.destroy();
|
|
35
|
+
resolve();
|
|
36
|
+
});
|
|
37
|
+
socket.on('error', () => {
|
|
38
|
+
socket.destroy();
|
|
39
|
+
if (attempts >= maxAttempts) return reject(new Error(`Port ${port} not ready after ${maxAttempts} attempts`));
|
|
40
|
+
setTimeout(tryConnect, interval);
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
tryConnect();
|
|
44
|
+
});
|
|
45
|
+
|
|
26
46
|
const logger = loggerFactory(import.meta);
|
|
27
47
|
|
|
28
48
|
/**
|
|
@@ -73,7 +93,6 @@ const logger = loggerFactory(import.meta);
|
|
|
73
93
|
* @property {boolean} kubeadm - Whether to run in kubeadm mode.
|
|
74
94
|
* @property {boolean} kind - Whether to run in kind mode.
|
|
75
95
|
* @property {boolean} k3s - Whether to run in k3s mode.
|
|
76
|
-
* @property {string} logType - The type of log to generate.
|
|
77
96
|
* @property {string} hosts - The hosts to use.
|
|
78
97
|
* @property {string} deployId - The deployment ID.
|
|
79
98
|
* @property {string} instanceId - The instance ID.
|
|
@@ -91,6 +110,10 @@ const logger = loggerFactory(import.meta);
|
|
|
91
110
|
* @property {string|Array<{ip: string, hostnames: string[]}>} hostAliases - Adds entries to the Pod /etc/hosts via Kubernetes hostAliases.
|
|
92
111
|
* As a string (CLI): semicolon-separated entries of "ip=hostname1,hostname2" (e.g., "127.0.0.1=foo.local,bar.local;10.1.2.3=foo.remote").
|
|
93
112
|
* As an array (programmatic): objects with `ip` and `hostnames` fields (e.g., [{ ip: "127.0.0.1", hostnames: ["foo.local"] }]).
|
|
113
|
+
* @property {boolean} gitClean - Whether to perform a `git clean` before running.
|
|
114
|
+
* @property {boolean} copy - Whether to copy the command to the clipboard instead of executing it.
|
|
115
|
+
* @property {boolean} skipFullBuild - Whether to skip the full client bundle build during deployment (supported by: sync, template-deploy).
|
|
116
|
+
* @property {boolean} pullBundle - Whether to pull the bundle before running. Use together with --skip-full-build to skip the local build entirely (supported by: sync, template-deploy).
|
|
94
117
|
* @memberof UnderpostRun
|
|
95
118
|
*/
|
|
96
119
|
const DEFAULT_OPTION = {
|
|
@@ -138,7 +161,6 @@ const DEFAULT_OPTION = {
|
|
|
138
161
|
kubeadm: false,
|
|
139
162
|
kind: false,
|
|
140
163
|
k3s: false,
|
|
141
|
-
logType: '',
|
|
142
164
|
hosts: '',
|
|
143
165
|
deployId: '',
|
|
144
166
|
instanceId: '',
|
|
@@ -154,6 +176,10 @@ const DEFAULT_OPTION = {
|
|
|
154
176
|
createJobNow: false,
|
|
155
177
|
fromNCommit: 0,
|
|
156
178
|
hostAliases: '',
|
|
179
|
+
gitClean: false,
|
|
180
|
+
copy: false,
|
|
181
|
+
skipFullBuild: false,
|
|
182
|
+
pullBundle: false,
|
|
157
183
|
};
|
|
158
184
|
|
|
159
185
|
/**
|
|
@@ -246,8 +272,15 @@ class UnderpostRun {
|
|
|
246
272
|
const ports = '6379,27017';
|
|
247
273
|
shellExec(`node bin run kill '${ports}'`);
|
|
248
274
|
shellExec(`node bin run dev-cluster --dev --expose --namespace ${options.namespace}`, { async: true });
|
|
249
|
-
|
|
250
|
-
|
|
275
|
+
logger.info('Waiting for port-forward services to be ready...');
|
|
276
|
+
try {
|
|
277
|
+
await Promise.all([waitForPort(27017), waitForPort(6379)]);
|
|
278
|
+
logger.info('Port-forward services are ready');
|
|
279
|
+
} catch (err) {
|
|
280
|
+
logger.error('Port-forward services failed to become ready', { error: err.message });
|
|
281
|
+
shellExec(`node bin run kill '${ports}'`);
|
|
282
|
+
throw err;
|
|
283
|
+
}
|
|
251
284
|
shellExec(`node bin metadata --generate ${path}`);
|
|
252
285
|
shellExec(`node bin db --dev --clean-fs-collection dd`);
|
|
253
286
|
shellExec(`node bin run kill '${ports}'`);
|
|
@@ -373,8 +406,12 @@ class UnderpostRun {
|
|
|
373
406
|
}
|
|
374
407
|
shellExec(`${baseCommand} run pull`);
|
|
375
408
|
|
|
376
|
-
// Capture last N commit messages for propagation
|
|
377
|
-
|
|
409
|
+
// Capture last N commit messages for propagation.
|
|
410
|
+
// When --from-n-commit is not set, auto-detect unpushed commit count (same as --unpush flag).
|
|
411
|
+
const fromN =
|
|
412
|
+
options.fromNCommit && parseInt(options.fromNCommit) > 0
|
|
413
|
+
? parseInt(options.fromNCommit)
|
|
414
|
+
: Underpost.repo.getUnpushedCount('.').count;
|
|
378
415
|
const message = shellExec(`node bin cmt --changelog ${fromN} --changelog-no-hash`, {
|
|
379
416
|
silent: true,
|
|
380
417
|
stdout: true,
|
|
@@ -404,6 +441,15 @@ class UnderpostRun {
|
|
|
404
441
|
deployType = 'init';
|
|
405
442
|
}
|
|
406
443
|
|
|
444
|
+
// If --build is set and path is a sync-engine-* target, push the pre-built client bundle
|
|
445
|
+
// to Cloudinary so the remote container can pull it instead of rebuilding from source.
|
|
446
|
+
if (options.build && deployConfId && deployConfId.startsWith('engine-')) {
|
|
447
|
+
const confName = deployConfId.replace(/^engine-/, '');
|
|
448
|
+
const pushDeployId = options.deployId || `dd-${confName}`;
|
|
449
|
+
logger.info(`[template-deploy] Running push-bundle for deployId: ${pushDeployId}`);
|
|
450
|
+
shellExec(`${baseCommand} run push-bundle --deploy-id ${pushDeployId}`);
|
|
451
|
+
}
|
|
452
|
+
|
|
407
453
|
// Dispatch npmpkg CI workflow — this builds pwa-microservices-template first.
|
|
408
454
|
// If deployConfId is set, npmpkg.ci.yml will dispatch the engine-<conf-id> CI
|
|
409
455
|
// with sync=true after template build completes. The engine CI then dispatches
|
|
@@ -424,16 +470,50 @@ class UnderpostRun {
|
|
|
424
470
|
},
|
|
425
471
|
|
|
426
472
|
/**
|
|
427
|
-
* @method template-deploy-
|
|
428
|
-
* @description
|
|
473
|
+
* @method template-deploy-local
|
|
474
|
+
* @description Similar to `template-deploy` but runs the workflow locally without dispatching GitHub Actions. It pulls the latest changes, pushes to GitHub, builds the template, and optionally triggers a local release with CI push.
|
|
475
|
+
* @param {string} path - The deployment path identifier (e.g., 'sync-engine-core', 'init-engine-core', or empty for build-only).
|
|
476
|
+
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
477
|
+
* @memberof UnderpostRun
|
|
478
|
+
*/
|
|
479
|
+
'template-deploy-local': async (path, options = DEFAULT_OPTION) => {
|
|
480
|
+
const baseCommand = options.dev ? 'node bin' : 'underpost';
|
|
481
|
+
shellExec(`npm run security:secrets`);
|
|
482
|
+
const reportPath = './gitleaks-report.json';
|
|
483
|
+
if (fs.existsSync(reportPath) && JSON.parse(fs.readFileSync(reportPath, 'utf8')).length > 0) {
|
|
484
|
+
logger.error('Secrets detected in gitleaks-report.json, aborting template-deploy');
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
shellExec(`${baseCommand} run pull`);
|
|
488
|
+
|
|
489
|
+
// Capture last N commit messages from the engine repo.
|
|
490
|
+
// When --from-n-commit is not set, auto-detect unpushed commit count (same as --unpush flag).
|
|
491
|
+
const fromN =
|
|
492
|
+
options.fromNCommit && parseInt(options.fromNCommit) > 0
|
|
493
|
+
? parseInt(options.fromNCommit)
|
|
494
|
+
: Underpost.repo.getUnpushedCount('.').count;
|
|
495
|
+
const rawMessage = shellExec(`node bin cmt --changelog ${fromN} --changelog-no-hash`, {
|
|
496
|
+
silent: true,
|
|
497
|
+
stdout: true,
|
|
498
|
+
}).trim();
|
|
499
|
+
const sanitizedMessage = Underpost.repo.sanitizeChangelogMessage(rawMessage);
|
|
500
|
+
|
|
501
|
+
const { triggerCmd } = path
|
|
502
|
+
? await Underpost.release.ci(path, sanitizedMessage, options)
|
|
503
|
+
: await Underpost.release.pwa(sanitizedMessage, options);
|
|
504
|
+
pbcopy(triggerCmd + ' && cd /home/dd/engine');
|
|
505
|
+
},
|
|
506
|
+
/**
|
|
507
|
+
* @method docker-image
|
|
508
|
+
* @description Dispatches the Docker image CI workflow (`docker-image.ci.yml`) for the `engine` repository via `workflow_dispatch`.
|
|
429
509
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
430
510
|
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
431
511
|
* @memberof UnderpostRun
|
|
432
512
|
*/
|
|
433
|
-
'
|
|
513
|
+
'docker-image': (path, options = DEFAULT_OPTION) => {
|
|
434
514
|
Underpost.repo.dispatchWorkflow({
|
|
435
515
|
repo: `${process.env.GITHUB_USERNAME}/engine`,
|
|
436
|
-
workflowFile:
|
|
516
|
+
workflowFile: `docker-image${path ? `.${path}` : ''}.ci.yml`,
|
|
437
517
|
ref: 'master',
|
|
438
518
|
inputs: {},
|
|
439
519
|
});
|
|
@@ -570,8 +650,9 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
570
650
|
sync: async (path, options = DEFAULT_OPTION) => {
|
|
571
651
|
// Dev usage: node bin run --dev --build sync dd-default
|
|
572
652
|
const env = options.dev ? 'development' : 'production';
|
|
573
|
-
const baseCommand = options.dev ? 'node bin' : 'underpost';
|
|
653
|
+
const baseCommand = 'node bin'; // options.dev ? 'node bin' : 'underpost';
|
|
574
654
|
const baseClusterCommand = options.dev ? ' --dev' : '';
|
|
655
|
+
const clusterFlag = options.k3s ? ' --k3s' : options.kind ? ' --kind' : ' --kubeadm';
|
|
575
656
|
const defaultPath = [
|
|
576
657
|
'dd-default',
|
|
577
658
|
options.replicas,
|
|
@@ -586,13 +667,23 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
586
667
|
image = image ? image : defaultPath[3];
|
|
587
668
|
node = node ? node : defaultPath[4];
|
|
588
669
|
shellExec(`${baseCommand} cluster --ns-use ${options.namespace}`);
|
|
670
|
+
|
|
671
|
+
if (image && !image.startsWith('localhost'))
|
|
672
|
+
Underpost.image.pullDockerHubImage({
|
|
673
|
+
dockerhubImage: image,
|
|
674
|
+
kind: options.kind || (!options.nodeName && !options.kubeadm && !options.k3s),
|
|
675
|
+
kubeadm: options.nodeName || options.kubeadm,
|
|
676
|
+
k3s: options.k3s,
|
|
677
|
+
});
|
|
678
|
+
|
|
589
679
|
if (isDeployRunnerContext(path, options)) {
|
|
590
680
|
if (!options.disablePrivateConfUpdate) {
|
|
591
681
|
const { validVersion } = Underpost.repo.privateConfUpdate(deployId);
|
|
592
682
|
if (!validVersion) throw new Error('Version mismatch');
|
|
593
683
|
}
|
|
594
684
|
if (options.timezone !== 'none') shellExec(`${baseCommand} run${baseClusterCommand} tz`);
|
|
595
|
-
if (options.deployIdCronJobs !== 'none')
|
|
685
|
+
if (options.deployIdCronJobs !== 'none')
|
|
686
|
+
shellExec(`node bin cron${baseClusterCommand}${clusterFlag} --setup-start --git --apply`);
|
|
596
687
|
}
|
|
597
688
|
|
|
598
689
|
const currentTraffic = isDeployRunnerContext(path, options)
|
|
@@ -605,20 +696,28 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
605
696
|
const cmdString = options.cmd
|
|
606
697
|
? ' --cmd ' + (options.cmd.find((c) => c.match('"')) ? '"' + options.cmd + '"' : "'" + options.cmd + "'")
|
|
607
698
|
: '';
|
|
699
|
+
const gitCleanFlag = options.gitClean ? ' --git-clean' : '';
|
|
700
|
+
|
|
701
|
+
const skipFullBuildFlag = options.skipFullBuild ? ' --skip-full-build' : '';
|
|
702
|
+
const pullBundleFlag = options.pullBundle ? ' --pull-bundle' : '';
|
|
608
703
|
|
|
609
704
|
shellExec(
|
|
610
|
-
`${baseCommand} deploy --
|
|
705
|
+
`${baseCommand} deploy${clusterFlag} --build-manifest --sync --info-router --replicas ${replicas} --node ${node}${
|
|
611
706
|
image ? ` --image ${image}` : ''
|
|
612
707
|
}${versions ? ` --versions ${versions}` : ''}${
|
|
613
708
|
options.namespace ? ` --namespace ${options.namespace}` : ''
|
|
614
|
-
}${timeoutFlags}${cmdString} ${deployId} ${env}`,
|
|
709
|
+
}${timeoutFlags}${cmdString}${gitCleanFlag}${skipFullBuildFlag}${pullBundleFlag} ${deployId} ${env}`,
|
|
615
710
|
);
|
|
616
711
|
|
|
617
712
|
if (isDeployRunnerContext(path, options)) {
|
|
713
|
+
// Backup app/services repositories with repo-backup configured
|
|
618
714
|
shellExec(
|
|
619
|
-
`${baseCommand}
|
|
715
|
+
`${baseCommand} db ${deployId} ${clusterFlag}${baseClusterCommand} --repo-backup --primary-pod --git --force-clone --preserveUUID ${options.namespace ? ` --ns ${options.namespace}` : ''}`,
|
|
716
|
+
);
|
|
717
|
+
shellExec(
|
|
718
|
+
`${baseCommand} deploy${clusterFlag}${cmdString} --replicas ${replicas} --disable-update-proxy ${deployId} ${env} --versions ${versions}${
|
|
620
719
|
options.namespace ? ` --namespace ${options.namespace}` : ''
|
|
621
|
-
}${timeoutFlags}`,
|
|
720
|
+
}${timeoutFlags}${gitCleanFlag}`,
|
|
622
721
|
);
|
|
623
722
|
if (!targetTraffic)
|
|
624
723
|
targetTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace });
|
|
@@ -830,6 +929,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
830
929
|
const confInstances = JSON.parse(
|
|
831
930
|
fs.readFileSync(`./engine-private/conf/${deployId}/conf.instances.json`, 'utf8'),
|
|
832
931
|
);
|
|
932
|
+
let promotedTraffic = '';
|
|
833
933
|
for (const instance of confInstances) {
|
|
834
934
|
let {
|
|
835
935
|
id: _id,
|
|
@@ -838,17 +938,23 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
838
938
|
image: _image,
|
|
839
939
|
fromPort: _fromPort,
|
|
840
940
|
toPort: _toPort,
|
|
941
|
+
fromDebugPort: _fromDebugPort,
|
|
942
|
+
toDebugPort: _toDebugPort,
|
|
841
943
|
cmd: _cmd,
|
|
842
944
|
volumes: _volumes,
|
|
843
945
|
metadata: _metadata,
|
|
844
946
|
} = instance;
|
|
845
947
|
if (id !== _id) continue;
|
|
846
948
|
const _deployId = `${deployId}-${_id}`;
|
|
949
|
+
// Use debug ports in development when defined, fall back to production ports.
|
|
950
|
+
if (env === 'development' && _fromDebugPort) _fromPort = _fromDebugPort;
|
|
951
|
+
if (env === 'development' && _toDebugPort) _toPort = _toDebugPort;
|
|
847
952
|
const currentTraffic = Underpost.deploy.getCurrentTraffic(_deployId, {
|
|
848
953
|
hostTest: _host,
|
|
849
954
|
namespace: options.namespace,
|
|
850
955
|
});
|
|
851
956
|
const targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : 'blue';
|
|
957
|
+
promotedTraffic = targetTraffic;
|
|
852
958
|
let proxyYaml =
|
|
853
959
|
Underpost.deploy.baseProxyYamlFactory({ host: _host, env: options.tls ? 'production' : env, options }) +
|
|
854
960
|
Underpost.deploy.deploymentYamlServiceFactory({
|
|
@@ -874,6 +980,18 @@ EOF
|
|
|
874
980
|
{ disableLog: true },
|
|
875
981
|
);
|
|
876
982
|
}
|
|
983
|
+
// Refresh the gRPC service to ensure it points to the parent deploy's current traffic.
|
|
984
|
+
if (promotedTraffic) {
|
|
985
|
+
const parentTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace }) || 'blue';
|
|
986
|
+
const grpcServicePath = Underpost.deploy.buildGrpcServiceManifest({
|
|
987
|
+
deployId,
|
|
988
|
+
env,
|
|
989
|
+
confServer: loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`),
|
|
990
|
+
namespace: options.namespace,
|
|
991
|
+
traffic: [parentTraffic],
|
|
992
|
+
});
|
|
993
|
+
if (grpcServicePath) shellExec(`kubectl apply -f ${grpcServicePath} -n ${options.namespace}`);
|
|
994
|
+
}
|
|
877
995
|
},
|
|
878
996
|
|
|
879
997
|
/**
|
|
@@ -900,12 +1018,17 @@ EOF
|
|
|
900
1018
|
image: _image,
|
|
901
1019
|
fromPort: _fromPort,
|
|
902
1020
|
toPort: _toPort,
|
|
1021
|
+
fromDebugPort: _fromDebugPort,
|
|
1022
|
+
toDebugPort: _toDebugPort,
|
|
903
1023
|
cmd: _cmd,
|
|
904
1024
|
volumes: _volumes,
|
|
905
1025
|
metadata: _metadata,
|
|
906
1026
|
} = instance;
|
|
907
1027
|
if (id !== _id) continue;
|
|
908
1028
|
const _deployId = `${deployId}-${_id}`;
|
|
1029
|
+
// Use debug ports in development when defined, fall back to production ports.
|
|
1030
|
+
if (env === 'development' && _fromDebugPort) _fromPort = _fromDebugPort;
|
|
1031
|
+
if (env === 'development' && _toDebugPort) _toPort = _toDebugPort;
|
|
909
1032
|
etcHosts.push(_host);
|
|
910
1033
|
if (options.expose) continue;
|
|
911
1034
|
// Examples images:
|
|
@@ -913,12 +1036,13 @@ EOF
|
|
|
913
1036
|
// `localhost/rockylinux9-underpost:${Underpost.version}`
|
|
914
1037
|
if (!_image) _image = `underpost/underpost-engine:${Underpost.version}`;
|
|
915
1038
|
|
|
916
|
-
if (
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
1039
|
+
if (_image && !_image.startsWith('localhost'))
|
|
1040
|
+
Underpost.image.pullDockerHubImage({
|
|
1041
|
+
dockerhubImage: _image,
|
|
1042
|
+
kind: options.kind || (!options.nodeName && !options.kubeadm && !options.k3s),
|
|
1043
|
+
kubeadm: options.nodeName || options.kubeadm,
|
|
1044
|
+
k3s: options.k3s,
|
|
1045
|
+
});
|
|
922
1046
|
|
|
923
1047
|
const currentTraffic = Underpost.deploy.getCurrentTraffic(_deployId, {
|
|
924
1048
|
hostTest: _host,
|
|
@@ -927,7 +1051,7 @@ EOF
|
|
|
927
1051
|
|
|
928
1052
|
const targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : 'blue';
|
|
929
1053
|
const podId = `${_deployId}-${env}-${targetTraffic}`;
|
|
930
|
-
const ignorePods = Underpost.
|
|
1054
|
+
const ignorePods = Underpost.kubectl.get(podId, 'pods', options.namespace).map((p) => p.NAME);
|
|
931
1055
|
Underpost.deploy.configMap(env, options.namespace);
|
|
932
1056
|
shellExec(`kubectl delete service ${podId}-service --namespace ${options.namespace} --ignore-not-found`);
|
|
933
1057
|
shellExec(`kubectl delete deployment ${podId} --namespace ${options.namespace} --ignore-not-found`);
|
|
@@ -939,7 +1063,30 @@ EOF
|
|
|
939
1063
|
env,
|
|
940
1064
|
version: targetTraffic,
|
|
941
1065
|
nodeName: options.nodeName,
|
|
1066
|
+
clusterContext: options.k3s ? 'k3s' : options.kubeadm ? 'kubeadm' : 'kind',
|
|
1067
|
+
gitClean: options.gitClean || false,
|
|
942
1068
|
});
|
|
1069
|
+
// Regenerate the parent deploy's gRPC ClusterIP service pointing to the
|
|
1070
|
+
// parent's current traffic colour and apply it before the instance pod starts so
|
|
1071
|
+
// DNS is resolvable the moment the pod boots.
|
|
1072
|
+
const parentTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace }) || 'blue';
|
|
1073
|
+
const grpcServicePath = Underpost.deploy.buildGrpcServiceManifest({
|
|
1074
|
+
deployId,
|
|
1075
|
+
env,
|
|
1076
|
+
confServer: loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`),
|
|
1077
|
+
namespace: options.namespace,
|
|
1078
|
+
traffic: [targetTraffic],
|
|
1079
|
+
host: _host,
|
|
1080
|
+
});
|
|
1081
|
+
if (grpcServicePath) shellExec(`kubectl apply -f ${grpcServicePath} -n ${options.namespace}`);
|
|
1082
|
+
|
|
1083
|
+
const resolvedCmd = _cmd[env].map((c) =>
|
|
1084
|
+
c.replaceAll(
|
|
1085
|
+
'{{grpc-service-dns}}',
|
|
1086
|
+
`${deployId}-grpc-service-${env}-${parentTraffic}.${options.namespace || 'default'}.svc.cluster.local:50051`,
|
|
1087
|
+
),
|
|
1088
|
+
);
|
|
1089
|
+
|
|
943
1090
|
let deploymentYaml = `---
|
|
944
1091
|
${Underpost.deploy
|
|
945
1092
|
.deploymentYamlPartsFactory({
|
|
@@ -951,7 +1098,7 @@ ${Underpost.deploy
|
|
|
951
1098
|
image: _image,
|
|
952
1099
|
namespace: options.namespace,
|
|
953
1100
|
volumes: _volumes,
|
|
954
|
-
cmd:
|
|
1101
|
+
cmd: resolvedCmd,
|
|
955
1102
|
})
|
|
956
1103
|
.replace('{{ports}}', buildKindPorts(_fromPort, _toPort))}
|
|
957
1104
|
`;
|
|
@@ -969,7 +1116,6 @@ EOF
|
|
|
969
1116
|
targetTraffic,
|
|
970
1117
|
ignorePods,
|
|
971
1118
|
options.namespace,
|
|
972
|
-
options.logType,
|
|
973
1119
|
);
|
|
974
1120
|
|
|
975
1121
|
if (!ready) {
|
|
@@ -989,6 +1135,153 @@ EOF
|
|
|
989
1135
|
}
|
|
990
1136
|
},
|
|
991
1137
|
|
|
1138
|
+
/**
|
|
1139
|
+
* @method instance-build-manifest
|
|
1140
|
+
* @description Builds a Kubernetes Deployment + Service manifest for a specific instance entry
|
|
1141
|
+
* from `conf.instances.json` and writes it to a file.
|
|
1142
|
+
* Traffic colour is automatically chosen as the opposite of the current live colour (blue/green),
|
|
1143
|
+
* defaulting to `blue` when no deployment is running yet.
|
|
1144
|
+
*
|
|
1145
|
+
* If `--build` is supplied the image is built from the project Dockerfile and loaded into the
|
|
1146
|
+
* cluster before the manifest is written (kind by default; `--kubeadm` / `--k3s` override).
|
|
1147
|
+
*
|
|
1148
|
+
* @param {string} path - Comma-separated: `deployId,instanceId[,projectPath]`.
|
|
1149
|
+
* `projectPath` is the root directory that contains the `Dockerfile` (e.g. `./cyberia-client`).
|
|
1150
|
+
* Artifacts are written to `<projectPath>/manifests/<env>/Dockerfile` and
|
|
1151
|
+
* `<projectPath>/manifests/<env>/deployment.yaml`.
|
|
1152
|
+
* In production, files are also copied to `<projectPath>/Dockerfile` and
|
|
1153
|
+
* `<projectPath>/deployment.yaml`.
|
|
1154
|
+
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
1155
|
+
* @memberof UnderpostRun
|
|
1156
|
+
*/
|
|
1157
|
+
'instance-build-manifest': (path, options = DEFAULT_OPTION) => {
|
|
1158
|
+
const env = options.dev ? 'development' : 'production';
|
|
1159
|
+
let [deployId, id, projectPath] = path.split(',');
|
|
1160
|
+
const rootPath = projectPath ? projectPath : '.';
|
|
1161
|
+
const envManifestPath = `${rootPath}/manifests/deployments/${id}-${env}`;
|
|
1162
|
+
const outputPath = `${envManifestPath}/deployment.yaml`;
|
|
1163
|
+
const dockerfileManifestPath = `${envManifestPath}/Dockerfile`;
|
|
1164
|
+
|
|
1165
|
+
fs.mkdirpSync(envManifestPath);
|
|
1166
|
+
|
|
1167
|
+
const confInstances = JSON.parse(
|
|
1168
|
+
fs.readFileSync(`./engine-private/conf/${deployId}/conf.instances.json`, 'utf8'),
|
|
1169
|
+
);
|
|
1170
|
+
|
|
1171
|
+
const instance = confInstances.find((i) => i.id === id);
|
|
1172
|
+
if (!instance) {
|
|
1173
|
+
logger.error(`Instance with id '${id}' not found in conf.instances.json for deployId '${deployId}'`);
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
let {
|
|
1178
|
+
id: _id,
|
|
1179
|
+
host: _host,
|
|
1180
|
+
path: _path,
|
|
1181
|
+
image: _image,
|
|
1182
|
+
fromPort: _fromPort,
|
|
1183
|
+
toPort: _toPort,
|
|
1184
|
+
fromDebugPort: _fromDebugPort,
|
|
1185
|
+
toDebugPort: _toDebugPort,
|
|
1186
|
+
cmd: _cmd,
|
|
1187
|
+
volumes: _volumes,
|
|
1188
|
+
metadata: _metadata,
|
|
1189
|
+
runtime: _runtime,
|
|
1190
|
+
} = instance;
|
|
1191
|
+
|
|
1192
|
+
// Resolve Dockerfile source: use runtime-specific path when instance defines a runtime.
|
|
1193
|
+
const dockerfileSourcePath = _runtime ? `src/runtime/${_runtime}/Dockerfile` : `${rootPath}/Dockerfile`;
|
|
1194
|
+
if (fs.existsSync(dockerfileSourcePath)) {
|
|
1195
|
+
fs.copyFileSync(dockerfileSourcePath, dockerfileManifestPath);
|
|
1196
|
+
} else {
|
|
1197
|
+
logger.warn(`[instance-build-manifest] Dockerfile not found at ${dockerfileSourcePath}`);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
const _deployId = `${deployId}-${_id}`;
|
|
1201
|
+
if (!_image) _image = `underpost/underpost-engine:${Underpost.version}`;
|
|
1202
|
+
// Use debug ports in development when defined, fall back to production ports.
|
|
1203
|
+
if (env === 'development' && _fromDebugPort) _fromPort = _fromDebugPort;
|
|
1204
|
+
if (env === 'development' && _toDebugPort) _toPort = _toDebugPort;
|
|
1205
|
+
|
|
1206
|
+
// Build image from projectPath Dockerfile and load into cluster when --build is set.
|
|
1207
|
+
if (options.build && projectPath) {
|
|
1208
|
+
const isKind = !options.kubeadm && !options.k3s;
|
|
1209
|
+
Underpost.image.build({
|
|
1210
|
+
path: projectPath,
|
|
1211
|
+
imageName: _image,
|
|
1212
|
+
podmanSave: true,
|
|
1213
|
+
imagePath: projectPath,
|
|
1214
|
+
kind: isKind,
|
|
1215
|
+
kubeadm: !!options.kubeadm,
|
|
1216
|
+
k3s: !!options.k3s,
|
|
1217
|
+
reset: !!options.reset,
|
|
1218
|
+
dev: options.dev,
|
|
1219
|
+
});
|
|
1220
|
+
logger.info(`[instance-build-manifest] Image built and loaded`, {
|
|
1221
|
+
image: _image,
|
|
1222
|
+
cluster: isKind ? 'kind' : options.kubeadm ? 'kubeadm' : 'k3s',
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// Determine target traffic: opposite of current, or 'blue' if nothing is running yet.
|
|
1227
|
+
const currentTraffic = Underpost.deploy.getCurrentTraffic(_deployId, {
|
|
1228
|
+
hostTest: _host,
|
|
1229
|
+
namespace: options.namespace,
|
|
1230
|
+
});
|
|
1231
|
+
const targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : 'blue';
|
|
1232
|
+
|
|
1233
|
+
// Resolve {{grpc-service-dns}} using the parent deploy's current (or default) traffic.
|
|
1234
|
+
const parentTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace }) || 'blue';
|
|
1235
|
+
const resolvedCmd = _cmd[env].map((c) =>
|
|
1236
|
+
c.replaceAll(
|
|
1237
|
+
'{{grpc-service-dns}}',
|
|
1238
|
+
`${deployId}-grpc-service-${env}-${parentTraffic}.${options.namespace || 'default'}.svc.cluster.local:50051`,
|
|
1239
|
+
),
|
|
1240
|
+
);
|
|
1241
|
+
|
|
1242
|
+
const deploymentYaml =
|
|
1243
|
+
`---\n` +
|
|
1244
|
+
Underpost.deploy
|
|
1245
|
+
.deploymentYamlPartsFactory({
|
|
1246
|
+
deployId: _deployId,
|
|
1247
|
+
env,
|
|
1248
|
+
suffix: targetTraffic,
|
|
1249
|
+
resources: Underpost.deploy.resourcesFactory(options),
|
|
1250
|
+
replicas: options.replicas,
|
|
1251
|
+
image: _image,
|
|
1252
|
+
namespace: options.namespace,
|
|
1253
|
+
volumes: _volumes,
|
|
1254
|
+
cmd: resolvedCmd,
|
|
1255
|
+
})
|
|
1256
|
+
.replace('{{ports}}', buildKindPorts(_fromPort, _toPort));
|
|
1257
|
+
|
|
1258
|
+
fs.writeFileSync(outputPath, deploymentYaml, 'utf8');
|
|
1259
|
+
logger.info(`[instance-build-manifest] Manifest written to ${outputPath}`, {
|
|
1260
|
+
deployId: _deployId,
|
|
1261
|
+
env,
|
|
1262
|
+
traffic: targetTraffic,
|
|
1263
|
+
image: _image,
|
|
1264
|
+
});
|
|
1265
|
+
|
|
1266
|
+
if (env === 'production') {
|
|
1267
|
+
if (fs.existsSync(dockerfileManifestPath)) {
|
|
1268
|
+
fs.copyFileSync(dockerfileManifestPath, `${rootPath}/Dockerfile`);
|
|
1269
|
+
}
|
|
1270
|
+
fs.copyFileSync(outputPath, `${rootPath}/deployment.yaml`);
|
|
1271
|
+
logger.info('[instance-build-manifest] Production artifacts copied to project root', {
|
|
1272
|
+
rootPath,
|
|
1273
|
+
dockerfile: `${rootPath}/Dockerfile`,
|
|
1274
|
+
deployment: `${rootPath}/deployment.yaml`,
|
|
1275
|
+
});
|
|
1276
|
+
const ciSrc = `./.github/workflows/docker-image.${_runtime}.ci.yml`;
|
|
1277
|
+
if (fs.existsSync(ciSrc)) {
|
|
1278
|
+
if (!fs.existsSync(`${rootPath}/.github/workflows`)) fs.mkdirpSync(`${rootPath}/.github/workflows`);
|
|
1279
|
+
fs.copyFileSync(ciSrc, `${rootPath}/.github/workflows/docker-image.${_runtime}.ci.yml`);
|
|
1280
|
+
logger.info(`[instance-build-manifest] CI workflow copied`, { src: ciSrc });
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
},
|
|
1284
|
+
|
|
992
1285
|
/**
|
|
993
1286
|
* @method ls-deployments
|
|
994
1287
|
* @description Retrieves and logs a table of Kubernetes deployments using `Underpost.deploy.get`.
|
|
@@ -997,7 +1290,7 @@ EOF
|
|
|
997
1290
|
* @memberof UnderpostRun
|
|
998
1291
|
*/
|
|
999
1292
|
'ls-deployments': async (path, options = DEFAULT_OPTION) => {
|
|
1000
|
-
console.table(await Underpost.
|
|
1293
|
+
console.table(await Underpost.kubectl.get(path, 'deployments', options.namespace));
|
|
1001
1294
|
},
|
|
1002
1295
|
|
|
1003
1296
|
/**
|
|
@@ -1013,6 +1306,44 @@ EOF
|
|
|
1013
1306
|
shellExec(`${options.underpostRoot}/scripts/rocky-setup.sh${options.dev ? ' --install-dev' : ``}`);
|
|
1014
1307
|
},
|
|
1015
1308
|
|
|
1309
|
+
/**
|
|
1310
|
+
* @method install-crio
|
|
1311
|
+
* @description Installs and configures CRI-O as the container runtime for kubeadm clusters.
|
|
1312
|
+
* Adds the stable v1.33 CRI-O yum repository, installs the `cri-o` package, configures
|
|
1313
|
+
* the systemd cgroup driver, enables the `crio` service, and writes `/etc/crictl.yaml`
|
|
1314
|
+
* so that `crictl` targets the CRI-O socket by default.
|
|
1315
|
+
* @param {string} path - Unused.
|
|
1316
|
+
* @param {Object} options - The default underpost runner options for customizing workflow.
|
|
1317
|
+
* @memberof UnderpostRun
|
|
1318
|
+
*/
|
|
1319
|
+
'install-crio': (path, options = DEFAULT_OPTION) => {
|
|
1320
|
+
logger.info('Installing CRI-O...');
|
|
1321
|
+
shellExec(`cat <<EOF | sudo tee /etc/yum.repos.d/cri-o.repo
|
|
1322
|
+
[cri-o]
|
|
1323
|
+
name=CRI-O
|
|
1324
|
+
baseurl=https://download.opensuse.org/repositories/isv:/cri-o:/stable:/v1.33/rpm/
|
|
1325
|
+
enabled=1
|
|
1326
|
+
gpgcheck=1
|
|
1327
|
+
gpgkey=https://download.opensuse.org/repositories/isv:/cri-o:/stable:/v1.33/rpm/repodata/repomd.xml.key
|
|
1328
|
+
EOF`);
|
|
1329
|
+
shellExec(`sudo dnf -y install cri-o`);
|
|
1330
|
+
// crictl is in the kubernetes repo but excluded by default — install it explicitly
|
|
1331
|
+
shellExec(`sudo yum install -y cri-tools --disableexcludes=kubernetes`);
|
|
1332
|
+
// Ensure CRI-O uses systemd cgroup driver (matches kubelet default)
|
|
1333
|
+
shellExec(
|
|
1334
|
+
`sudo sed -i 's/^#\?cgroup_manager =.*/cgroup_manager = "systemd"/' /etc/crio/crio.conf 2>/dev/null || true`,
|
|
1335
|
+
);
|
|
1336
|
+
shellExec(`sudo systemctl enable --now crio`);
|
|
1337
|
+
logger.info('CRI-O installed and started.');
|
|
1338
|
+
// Write crictl config so all crictl calls default to the CRI-O socket
|
|
1339
|
+
shellExec(`cat <<EOF | sudo tee /etc/crictl.yaml
|
|
1340
|
+
runtime-endpoint: unix:///var/run/crio/crio.sock
|
|
1341
|
+
image-endpoint: unix:///var/run/crio/crio.sock
|
|
1342
|
+
timeout: 10
|
|
1343
|
+
debug: false
|
|
1344
|
+
EOF`);
|
|
1345
|
+
},
|
|
1346
|
+
|
|
1016
1347
|
/**
|
|
1017
1348
|
* @method dd-container
|
|
1018
1349
|
* @description Deploys a development or debug container tasks jobs, setting up necessary volumes and images, and running specified commands within the container.
|
|
@@ -1091,15 +1422,13 @@ EOF
|
|
|
1091
1422
|
'db-client': async (path, options = DEFAULT_OPTION) => {
|
|
1092
1423
|
const { underpostRoot } = options;
|
|
1093
1424
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
}
|
|
1101
|
-
// For kubeadm/k3s, ensure it's available for containerd
|
|
1102
|
-
shellExec(`sudo crictl pull ${image}`);
|
|
1425
|
+
Underpost.image.pullDockerHubImage({
|
|
1426
|
+
dockerhubImage: 'adminer',
|
|
1427
|
+
version: '4.7.6-standalone',
|
|
1428
|
+
kind: options.kind,
|
|
1429
|
+
kubeadm: options.kubeadm,
|
|
1430
|
+
k3s: options.k3s,
|
|
1431
|
+
});
|
|
1103
1432
|
|
|
1104
1433
|
shellExec(`kubectl delete deployment adminer -n ${options.namespace} --ignore-not-found`);
|
|
1105
1434
|
shellExec(`kubectl apply -k ${underpostRoot}/manifests/deployment/adminer/. -n ${options.namespace}`);
|
|
@@ -1157,6 +1486,9 @@ EOF
|
|
|
1157
1486
|
/**
|
|
1158
1487
|
* @method promote
|
|
1159
1488
|
* @description Switches traffic between blue/green deployments for a specified deployment ID(s) (uses `dd.router` for 'dd', or a specific ID).
|
|
1489
|
+
* When `--tls` is set, rebuilds the proxy manifest with `--cert` so the HTTPProxy includes
|
|
1490
|
+
* TLS config, deletes stale Certificate resources, then reapplies the proxy and secret.yaml
|
|
1491
|
+
* (cert-manager Certificate resources) for each affected deployment.
|
|
1160
1492
|
* @param {string} path - The input value, identifier, or path for the operation (used as a comma-separated string: `deployId,env,replicas`).
|
|
1161
1493
|
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
1162
1494
|
* @memberof UnderpostRun
|
|
@@ -1165,11 +1497,34 @@ EOF
|
|
|
1165
1497
|
let [inputDeployId, inputEnv, inputReplicas] = path.split(',');
|
|
1166
1498
|
if (!inputEnv) inputEnv = 'production';
|
|
1167
1499
|
if (!inputReplicas) inputReplicas = 1;
|
|
1500
|
+
// TODO: normalize: --tls maps to --cert for deploy.js isValidTLSContext compatibility
|
|
1501
|
+
if (options.tls) options.cert = true;
|
|
1502
|
+
|
|
1503
|
+
const applyCerts = (deployId, targetTraffic) => {
|
|
1504
|
+
if (!options.tls) return;
|
|
1505
|
+
// Rebuild proxy.yaml with --cert so the HTTPProxy includes TLS virtualhost config
|
|
1506
|
+
shellExec(
|
|
1507
|
+
`node bin deploy --build-manifest --cert --traffic ${targetTraffic} --replicas ${inputReplicas} --namespace ${options.namespace} ${deployId} ${inputEnv}`,
|
|
1508
|
+
);
|
|
1509
|
+
// Delete stale Certificate resources before reapplying
|
|
1510
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
1511
|
+
if (fs.existsSync(confServerPath)) {
|
|
1512
|
+
for (const host of Object.keys(JSON.parse(fs.readFileSync(confServerPath, 'utf8'))))
|
|
1513
|
+
shellExec(`sudo kubectl delete Certificate ${host} -n ${options.namespace} --ignore-not-found`);
|
|
1514
|
+
}
|
|
1515
|
+
shellExec(
|
|
1516
|
+
`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${inputEnv}/proxy.yaml -n ${options.namespace}`,
|
|
1517
|
+
);
|
|
1518
|
+
const secretPath = `./engine-private/conf/${deployId}/build/${inputEnv}/secret.yaml`;
|
|
1519
|
+
if (fs.existsSync(secretPath)) shellExec(`kubectl apply -f ${secretPath} -n ${options.namespace}`);
|
|
1520
|
+
};
|
|
1521
|
+
|
|
1168
1522
|
if (inputDeployId === 'dd') {
|
|
1169
1523
|
for (const deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
|
|
1170
1524
|
const currentTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace });
|
|
1171
1525
|
const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
|
|
1172
1526
|
Underpost.deploy.switchTraffic(deployId, inputEnv, targetTraffic, inputReplicas, options.namespace, options);
|
|
1527
|
+
applyCerts(deployId, targetTraffic);
|
|
1173
1528
|
}
|
|
1174
1529
|
} else {
|
|
1175
1530
|
const currentTraffic = Underpost.deploy.getCurrentTraffic(inputDeployId, { namespace: options.namespace });
|
|
@@ -1182,6 +1537,7 @@ EOF
|
|
|
1182
1537
|
options.namespace,
|
|
1183
1538
|
options,
|
|
1184
1539
|
);
|
|
1540
|
+
applyCerts(inputDeployId, targetTraffic);
|
|
1185
1541
|
}
|
|
1186
1542
|
},
|
|
1187
1543
|
/**
|
|
@@ -1662,7 +2018,7 @@ EOF
|
|
|
1662
2018
|
: [
|
|
1663
2019
|
`npm install -g npm@11.2.0`,
|
|
1664
2020
|
`npm install -g underpost`,
|
|
1665
|
-
`${baseCommand} secret underpost --create-from-
|
|
2021
|
+
`${baseCommand} secret underpost --create-from-env`,
|
|
1666
2022
|
`${baseCommand} start --build --run ${deployId} ${env}`,
|
|
1667
2023
|
];
|
|
1668
2024
|
shellExec(`node bin run sync${baseClusterCommand} --deploy-id-cron-jobs none dd-test --cmd "${cmd}"`);
|
|
@@ -1693,7 +2049,7 @@ EOF
|
|
|
1693
2049
|
|
|
1694
2050
|
const { close } = await (async () => {
|
|
1695
2051
|
const checkAwaitPath = '/await';
|
|
1696
|
-
while (!Underpost.
|
|
2052
|
+
while (!Underpost.kubectl.existsFile({ podName, path: checkAwaitPath })) {
|
|
1697
2053
|
logger.info('monitor', checkAwaitPath);
|
|
1698
2054
|
await timer(1000);
|
|
1699
2055
|
}
|
|
@@ -1720,7 +2076,7 @@ EOF
|
|
|
1720
2076
|
logger.info('monitor', checkPath);
|
|
1721
2077
|
{
|
|
1722
2078
|
const checkAwaitPath = `/home/dd/docs${checkPath}`;
|
|
1723
|
-
while (!Underpost.
|
|
2079
|
+
while (!Underpost.kubectl.existsFile({ podName, path: checkAwaitPath })) {
|
|
1724
2080
|
logger.info('waiting for', checkAwaitPath);
|
|
1725
2081
|
await timer(1000);
|
|
1726
2082
|
}
|
|
@@ -1786,7 +2142,8 @@ EOF
|
|
|
1786
2142
|
|
|
1787
2143
|
shellCd(dir);
|
|
1788
2144
|
|
|
1789
|
-
|
|
2145
|
+
Underpost.repo.initLocalRepo({ path: dir });
|
|
2146
|
+
shellExec(`git add . && git commit -m "Base implementation"`);
|
|
1790
2147
|
shellExec(`chmod +x ./replace_params.sh`);
|
|
1791
2148
|
shellExec(`chmod +x ./build.sh`);
|
|
1792
2149
|
|
|
@@ -1795,6 +2152,16 @@ EOF
|
|
|
1795
2152
|
|
|
1796
2153
|
shellCd('/home/dd/engine');
|
|
1797
2154
|
},
|
|
2155
|
+
/**
|
|
2156
|
+
* @method pull-rocky-image
|
|
2157
|
+
* @description Pulls the base `rockylinux:9` image from Docker Hub via Podman.
|
|
2158
|
+
* @param {string} path - The input value, identifier, or path for the operation.
|
|
2159
|
+
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
2160
|
+
* @memberof UnderpostRun
|
|
2161
|
+
*/
|
|
2162
|
+
'pull-rocky-image': (path, options = DEFAULT_OPTION) => {
|
|
2163
|
+
shellExec(`sudo podman pull docker.io/library/rockylinux:9`);
|
|
2164
|
+
},
|
|
1798
2165
|
/**
|
|
1799
2166
|
* @method rmi
|
|
1800
2167
|
* @description Forces the removal of all local Podman images (`podman rmi $(podman images -qa) --force`).
|
|
@@ -1824,6 +2191,42 @@ EOF
|
|
|
1824
2191
|
} else shellExec(`sudo kill -9 $(lsof -t -i:${_path})`);
|
|
1825
2192
|
}
|
|
1826
2193
|
},
|
|
2194
|
+
/**
|
|
2195
|
+
* @method generate-pass
|
|
2196
|
+
* @description Generates a cryptographically secure random password that satisfies all validatePassword
|
|
2197
|
+
* constraints (lowercase, uppercase, digit, special char, min 8 chars). Logs the plain password
|
|
2198
|
+
* to the console or, when `--copy` is set, copies it to the clipboard via pbcopy.
|
|
2199
|
+
* @param {string} path - Optional password length (default: 16).
|
|
2200
|
+
* @param {Object} options - The default underpost runner options for customizing workflow.
|
|
2201
|
+
* @param {boolean} options.copy - When true, copies to clipboard instead of logging.
|
|
2202
|
+
* @memberof UnderpostRun
|
|
2203
|
+
*/
|
|
2204
|
+
'generate-pass': (path, options = DEFAULT_OPTION) => {
|
|
2205
|
+
const length = path && parseInt(path) > 0 ? parseInt(path) : 16;
|
|
2206
|
+
const lower = 'abcdefghijklmnopqrstuvwxyz';
|
|
2207
|
+
const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
2208
|
+
const digits = '0123456789';
|
|
2209
|
+
const special = '@#$%^&*()_+';
|
|
2210
|
+
const all = lower + upper + digits + special;
|
|
2211
|
+
const buf = crypto.randomBytes(length + 4);
|
|
2212
|
+
// Guarantee at least one character from each required class
|
|
2213
|
+
const chars = [
|
|
2214
|
+
lower[buf[0] % lower.length],
|
|
2215
|
+
upper[buf[1] % upper.length],
|
|
2216
|
+
digits[buf[2] % digits.length],
|
|
2217
|
+
special[buf[3] % special.length],
|
|
2218
|
+
];
|
|
2219
|
+
for (let i = 4; i < length; i++) chars.push(all[buf[i] % all.length]);
|
|
2220
|
+
// Fisher-Yates shuffle using an independent random buffer
|
|
2221
|
+
const shuf = crypto.randomBytes(length);
|
|
2222
|
+
for (let i = chars.length - 1; i > 0; i--) {
|
|
2223
|
+
const j = shuf[i % shuf.length] % (i + 1);
|
|
2224
|
+
[chars[i], chars[j]] = [chars[j], chars[i]];
|
|
2225
|
+
}
|
|
2226
|
+
const password = chars.join('');
|
|
2227
|
+
if (options.copy) pbcopy(password);
|
|
2228
|
+
else console.log(password);
|
|
2229
|
+
},
|
|
1827
2230
|
/**
|
|
1828
2231
|
* @method secret
|
|
1829
2232
|
* @description Creates an Underpost secret named 'underpost' from a file, defaulting to `/home/dd/engine/engine-private/conf/dd-cron/.env.production` if no path is provided.
|
|
@@ -1833,9 +2236,7 @@ EOF
|
|
|
1833
2236
|
*/
|
|
1834
2237
|
secret: (path, options = DEFAULT_OPTION) => {
|
|
1835
2238
|
const secretPath = path ? path : `/home/dd/engine/engine-private/conf/dd-cron/.env.production`;
|
|
1836
|
-
const command = options.dev
|
|
1837
|
-
? `node bin secret underpost --create-from-file ${secretPath}`
|
|
1838
|
-
: `underpost secret underpost --create-from-file ${secretPath}`;
|
|
2239
|
+
const command = `${options.dev ? 'node bin' : 'underpost'} secret underpost --create-from-file ${secretPath}`;
|
|
1839
2240
|
shellExec(command);
|
|
1840
2241
|
},
|
|
1841
2242
|
/**
|
|
@@ -2006,6 +2407,144 @@ EOF`;
|
|
|
2006
2407
|
if (options.logs) shellExec(`kubectl logs -f ${podName} -n ${namespace}`, { async: true });
|
|
2007
2408
|
}
|
|
2008
2409
|
},
|
|
2410
|
+
|
|
2411
|
+
/**
|
|
2412
|
+
* @method push-bundle
|
|
2413
|
+
* @description Builds the client zip for the specified deployment, splits it into parts, and uploads to file storage.
|
|
2414
|
+
* Steps: set env, build+split zip, switch to cron env, upload parts to storage.
|
|
2415
|
+
* @param {string} path - Optional `fsPath.splitOption` string.
|
|
2416
|
+
* Examples: `build` (default split 8), `build.16` (split 16 MB), `build.none-split` (no split flag).
|
|
2417
|
+
* @param {Object} options - The default underpost runner options for customizing workflow.
|
|
2418
|
+
* @param {string} [options.deployId] - Override deploy ID.
|
|
2419
|
+
* @param {boolean} [options.dev] - Use development environment; defaults to production.
|
|
2420
|
+
* @memberof UnderpostRun
|
|
2421
|
+
*/
|
|
2422
|
+
'push-bundle': (path = '', options = DEFAULT_OPTION) => {
|
|
2423
|
+
const baseCommand = options.dev ? 'node bin' : 'underpost';
|
|
2424
|
+
const env = options.dev ? 'development' : 'production';
|
|
2425
|
+
const deployId = options.deployId || 'dd-default';
|
|
2426
|
+
const pathParts = (path || '').split('.');
|
|
2427
|
+
const fsPath = (pathParts[0] || '').trim() || 'build';
|
|
2428
|
+
const splitOption = (pathParts[1] || '').trim();
|
|
2429
|
+
|
|
2430
|
+
let splitFlag = '--split 8';
|
|
2431
|
+
if (splitOption) {
|
|
2432
|
+
if (splitOption === 'none-split') {
|
|
2433
|
+
splitFlag = '';
|
|
2434
|
+
} else {
|
|
2435
|
+
const splitMb = Number(splitOption);
|
|
2436
|
+
if (Number.isFinite(splitMb) && splitMb > 0) {
|
|
2437
|
+
splitFlag = `--split ${splitMb}`;
|
|
2438
|
+
} else {
|
|
2439
|
+
logger.warn('push-bundle: invalid split option, using default split 8', {
|
|
2440
|
+
path,
|
|
2441
|
+
splitOption,
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
shellExec(`${baseCommand} env ${deployId} ${env}`);
|
|
2448
|
+
shellExec(`${baseCommand} client ${deployId} --build-zip${splitFlag ? ` ${splitFlag}` : ''}`);
|
|
2449
|
+
shellExec(
|
|
2450
|
+
`${baseCommand} fs ${fsPath} --recursive --deploy-id ${deployId} --storage-file-path engine-private/conf/${deployId}/storage.bundle.json --force`,
|
|
2451
|
+
);
|
|
2452
|
+
},
|
|
2453
|
+
|
|
2454
|
+
/**
|
|
2455
|
+
* @method pull-bundle
|
|
2456
|
+
* @description Downloads split zip parts from file storage, merges and extracts them, and moves the result into the public directory.
|
|
2457
|
+
* Steps: set env, download parts (omit-unzip), merge zip, unzip, remove zip + parts, move to public/<host>[/path].
|
|
2458
|
+
* Iterates over every non-singleReplica, non-redirect, non-disabledRebuild route in conf.server.json
|
|
2459
|
+
* so that multi-path deployments are handled correctly.
|
|
2460
|
+
* @param {string} path - Optional comma-separated host name(s) to restrict processing (e.g. 'underpost.net' or 'a.com,b.com').
|
|
2461
|
+
* If omitted, all hosts from `engine-private/conf/<deployId>/conf.server.json` are used.
|
|
2462
|
+
* @param {Object} options - The default underpost runner options for customizing workflow.
|
|
2463
|
+
* @param {string} [options.deployId] - Deploy ID for storage lookup (defaults to 'dd-default').
|
|
2464
|
+
* @param {boolean} [options.dev] - Use development environment; defaults to production.
|
|
2465
|
+
* @memberof UnderpostRun
|
|
2466
|
+
*/
|
|
2467
|
+
'pull-bundle': (path = '', options = DEFAULT_OPTION) => {
|
|
2468
|
+
const baseCommand = options.dev ? 'node bin' : 'underpost';
|
|
2469
|
+
const env = options.dev ? 'development' : 'production';
|
|
2470
|
+
const deployId = options.deployId || 'dd-default';
|
|
2471
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
2472
|
+
const confServer = fs.existsSync(confServerPath) ? loadConfServerJson(confServerPath) : {};
|
|
2473
|
+
const hostsArg = path
|
|
2474
|
+
? path
|
|
2475
|
+
.split(',')
|
|
2476
|
+
.map((h) => h.trim())
|
|
2477
|
+
.filter(Boolean)
|
|
2478
|
+
: Object.keys(confServer);
|
|
2479
|
+
|
|
2480
|
+
if (hostsArg.length === 0) {
|
|
2481
|
+
logger.error('pull-bundle: no hosts resolved', { deployId, path, confServerPath });
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
shellExec(`${baseCommand} env ${deployId} ${env}`);
|
|
2486
|
+
if (!fs.existsSync('./build')) fs.mkdirSync('./build', { recursive: true });
|
|
2487
|
+
shellExec(
|
|
2488
|
+
`${baseCommand} fs build --recursive --deploy-id ${deployId} --storage-file-path engine-private/conf/${deployId}/storage.bundle.json --pull --omit-unzip`,
|
|
2489
|
+
);
|
|
2490
|
+
|
|
2491
|
+
for (const host of hostsArg) {
|
|
2492
|
+
// Gather all routes for this host; fall back to root '/' when host is not in confServer
|
|
2493
|
+
// (e.g. when hosts were provided explicitly via the path argument).
|
|
2494
|
+
const routePaths = confServer[host] ? Object.keys(confServer[host]) : ['/'];
|
|
2495
|
+
|
|
2496
|
+
for (const routePath of routePaths) {
|
|
2497
|
+
const routeConf = confServer[host] ? confServer[host][routePath] || {} : {};
|
|
2498
|
+
// Skip routes that are not built by buildClient (mirrors buildClient skip conditions)
|
|
2499
|
+
if (routeConf.singleReplica || routeConf.redirect || routeConf.disabledRebuild) continue;
|
|
2500
|
+
|
|
2501
|
+
// buildClient names the zip as "<host>-<path-no-slashes>.zip"
|
|
2502
|
+
// e.g. host="underpost.net", path="/" → buildId="underpost.net-", zip="build/underpost.net-.zip"
|
|
2503
|
+
// e.g. host="app.net", path="/admin" → buildId="app.net-admin", zip="build/app.net-admin.zip"
|
|
2504
|
+
const buildId = `${host}-${routePath.replaceAll('/', '')}`;
|
|
2505
|
+
const zipPath = `build/${buildId}.zip`;
|
|
2506
|
+
const buildDir = './build';
|
|
2507
|
+
const hasZip = fs.existsSync(zipPath);
|
|
2508
|
+
const hasParts =
|
|
2509
|
+
fs.existsSync(buildDir) &&
|
|
2510
|
+
fs
|
|
2511
|
+
.readdirSync(buildDir)
|
|
2512
|
+
.some((name) => name.startsWith(`${buildId}.zip.part`) || name.startsWith(`${buildId}.zip-part`));
|
|
2513
|
+
|
|
2514
|
+
if (!hasZip && !hasParts) {
|
|
2515
|
+
logger.warn(`Bundle not found for '${host}${routePath}'. Skipping.`, { zipPath, deployId });
|
|
2516
|
+
continue;
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
if (hasParts) shellExec(`${baseCommand} client --merge-zip ${zipPath}`);
|
|
2520
|
+
shellExec(`${baseCommand} client --unzip ${zipPath}`);
|
|
2521
|
+
shellExec(`sudo rm -rf ${zipPath}`);
|
|
2522
|
+
|
|
2523
|
+
// Clean up downloaded part wrapper zips left by --omit-unzip pull
|
|
2524
|
+
if (fs.existsSync(buildDir)) {
|
|
2525
|
+
fs.readdirSync(buildDir)
|
|
2526
|
+
.filter((name) => name.startsWith(`${buildId}.zip.part`) || name.startsWith(`${buildId}.zip-part`))
|
|
2527
|
+
.forEach((partFile) => shellExec(`sudo rm -rf ${buildDir}/${partFile}`));
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
// unzipClientBuild extracts to buildId with trailing '-' stripped
|
|
2531
|
+
// e.g. "build/underpost.net-" → "build/underpost.net"
|
|
2532
|
+
// e.g. "build/app.net-admin" → "build/app.net-admin" (no trailing dash, no change)
|
|
2533
|
+
const extractedDir = `build/${buildId.replace(/-$/, '')}`;
|
|
2534
|
+
if (!fs.existsSync(extractedDir)) {
|
|
2535
|
+
logger.warn(`Extracted build dir not found: ${extractedDir}. Skipping move for '${host}${routePath}'.`);
|
|
2536
|
+
continue;
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
// Destination mirrors the public directory layout used by the server
|
|
2540
|
+
const publicDestPath = routePath === '/' ? `public/${host}` : `public/${host}${routePath}`;
|
|
2541
|
+
if (fs.existsSync(publicDestPath)) shellExec(`sudo rm -rf ${publicDestPath}`);
|
|
2542
|
+
// Ensure parent directory exists for sub-paths
|
|
2543
|
+
if (routePath !== '/') shellExec(`sudo mkdir -p public/${host}`);
|
|
2544
|
+
shellExec(`sudo mv ${extractedDir} ${publicDestPath}`);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
},
|
|
2009
2548
|
};
|
|
2010
2549
|
|
|
2011
2550
|
static API = {
|