@underpostnet/cyberia 3.2.80
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 +14 -0
- package/.env.example +144 -0
- package/.github/workflows/cyberia-client.cd.yml +40 -0
- package/.github/workflows/cyberia-server.cd.yml +40 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +71 -0
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +70 -0
- package/.github/workflows/docker-image.cyberia-server.ci.yml +71 -0
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +71 -0
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +53 -0
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +53 -0
- package/.github/workflows/engine-cyberia.cd.yml +157 -0
- package/.github/workflows/engine-cyberia.ci.yml +125 -0
- package/.github/workflows/ghpkg.ci.yml +160 -0
- package/.github/workflows/gitlab.ci.yml +20 -0
- package/.github/workflows/hardhat.ci.yml +82 -0
- package/.github/workflows/npmpkg.ci.yml +126 -0
- package/.github/workflows/publish.ci.yml +87 -0
- package/.github/workflows/publish.cyberia.ci.yml +79 -0
- package/.github/workflows/pwa-microservices-template-page.cd.yml +65 -0
- package/.github/workflows/pwa-microservices-template-test.ci.yml +33 -0
- package/.github/workflows/release.cd.yml +46 -0
- package/.nycrc +9 -0
- package/.prettierignore +12 -0
- package/.prettierrc +9 -0
- package/.vscode/extensions.json +10 -0
- package/.vscode/settings.json +26 -0
- package/CHANGELOG.md +2516 -0
- package/CLI-HELP.md +1099 -0
- package/Dockerfile +161 -0
- package/Dockerfile.dev +161 -0
- package/Dockerfile.test +165 -0
- package/LICENSE +21 -0
- package/README.md +259 -0
- package/baremetal/commission-workflows.json +211 -0
- package/baremetal/packer-workflows.json +24 -0
- package/bin/build.js +261 -0
- package/bin/build.template.js +33 -0
- package/bin/cyberia.js +5627 -0
- package/bin/deploy.js +1547 -0
- package/bin/index.js +5627 -0
- package/bump.config.js +27 -0
- package/compose.env +144 -0
- package/conf.js +952 -0
- package/deployment.yaml +418 -0
- package/docker-compose.yml +434 -0
- package/examples/static-page/README.md +150 -0
- package/examples/static-page/ssr-components/CustomPage.js +554 -0
- package/examples/static-page/static-config-example.json +57 -0
- package/hardhat/.env.example +31 -0
- package/hardhat/contracts/ObjectLayerToken.sol +391 -0
- package/hardhat/deployments/.gitkeep +0 -0
- package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
- package/hardhat/hardhat.config.js +136 -0
- package/hardhat/ignition/modules/.gitkeep +0 -0
- package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
- package/hardhat/networks/besu-object-layer.network.json +138 -0
- package/hardhat/package-lock.json +2871 -0
- package/hardhat/package.json +43 -0
- package/hardhat/scripts/deployObjectLayerToken.js +98 -0
- package/hardhat/test/ObjectLayerToken.js +696 -0
- 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/ipfs/configure-ipfs.sh +13 -0
- package/jsconfig.json +9 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +48 -0
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +48 -0
- package/manifests/deployment/adminer/deployment.yaml +32 -0
- package/manifests/deployment/adminer/kustomization.yaml +7 -0
- package/manifests/deployment/adminer/service.yaml +13 -0
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +418 -0
- package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
- package/manifests/deployment/dd-cyberia-development/proxy.yaml +189 -0
- package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +164 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +163 -0
- package/manifests/deployment/dd-default-development/proxy.yaml +46 -0
- package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
- package/manifests/deployment/fastapi/backend-service.yml +19 -0
- package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
- package/manifests/deployment/fastapi/frontend-service.yml +15 -0
- package/manifests/deployment/fastapi/initial_data.sh +8 -0
- package/manifests/deployment/kafka/deployment.yaml +67 -0
- package/manifests/deployment/mongo-express/deployment.yaml +62 -0
- package/manifests/deployment/phpmyadmin/deployment.yaml +54 -0
- package/manifests/deployment/playwright/deployment.yaml +52 -0
- package/manifests/deployment/spark/spark-pi-py.yaml +20 -0
- package/manifests/deployment/tensorflow/tf-gpu-test.yaml +63 -0
- package/manifests/envoy-service-nodeport.yaml +22 -0
- package/manifests/grafana/deployment.yaml +64 -0
- package/manifests/grafana/kustomization.yaml +7 -0
- package/manifests/grafana/pvc.yaml +12 -0
- package/manifests/grafana/service.yaml +14 -0
- package/manifests/grafana/storage-class.yaml +9 -0
- package/manifests/ipfs/configmap.yaml +64 -0
- package/manifests/ipfs/headless-service.yaml +35 -0
- package/manifests/ipfs/kustomization.yaml +8 -0
- package/manifests/ipfs/statefulset.yaml +149 -0
- package/manifests/ipfs/storage-class.yaml +9 -0
- package/manifests/kind-config-dev.yaml +20 -0
- package/manifests/kind-config.yaml +12 -0
- package/manifests/kubeadm-calico-config.yaml +14 -0
- package/manifests/letsencrypt-prod.yaml +14 -0
- package/manifests/lxd/lxd-admin-profile.yaml +26 -0
- package/manifests/lxd/lxd-preseed.yaml +30 -0
- package/manifests/mariadb/kustomization.yaml +9 -0
- package/manifests/mariadb/pv.yaml +12 -0
- package/manifests/mariadb/pvc.yaml +10 -0
- package/manifests/mariadb/service.yaml +10 -0
- package/manifests/mariadb/statefulset.yaml +56 -0
- package/manifests/mariadb/storage-class.yaml +10 -0
- package/manifests/mongodb/headless-service.yaml +10 -0
- package/manifests/mongodb/kustomization.yaml +11 -0
- package/manifests/mongodb/pv-pvc.yaml +59 -0
- package/manifests/mongodb/statefulset.yaml +113 -0
- package/manifests/mongodb/storage-class.yaml +9 -0
- package/manifests/mongodb-4.4/headless-service.yaml +10 -0
- package/manifests/mongodb-4.4/kustomization.yaml +9 -0
- package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
- package/manifests/mongodb-4.4/pv-pvc.yaml +19 -0
- package/manifests/mongodb-4.4/service-deployment.yaml +61 -0
- package/manifests/mongodb-4.4/statefulset.yaml +79 -0
- package/manifests/mongodb-4.4/storage-class.yaml +9 -0
- package/manifests/mysql/kustomization.yaml +7 -0
- package/manifests/mysql/pv-pvc.yaml +27 -0
- package/manifests/mysql/statefulset.yaml +55 -0
- package/manifests/postgresql/configmap.yaml +9 -0
- package/manifests/postgresql/kustomization.yaml +10 -0
- package/manifests/postgresql/pv.yaml +15 -0
- package/manifests/postgresql/pvc.yaml +13 -0
- package/manifests/postgresql/service.yaml +10 -0
- package/manifests/postgresql/statefulset.yaml +37 -0
- package/manifests/prometheus/deployment.yaml +82 -0
- package/manifests/pv-pvc-dd.yaml +34 -0
- package/manifests/valkey/kustomization.yaml +7 -0
- package/manifests/valkey/service.yaml +10 -0
- package/manifests/valkey/statefulset.yaml +37 -0
- package/manifests/valkey/valkey-nodeport.yaml +17 -0
- package/mongodb/entrypoint.sh +76 -0
- package/nginx.conf +128 -0
- package/nodemon.json +6 -0
- package/package.json +101 -0
- package/packer/images/Rocky9Amd64/Makefile +62 -0
- package/packer/images/Rocky9Amd64/QUICKSTART.md +113 -0
- package/packer/images/Rocky9Amd64/README.md +122 -0
- package/packer/images/Rocky9Amd64/http/rocky9.ks.pkrtpl.hcl +114 -0
- package/packer/images/Rocky9Amd64/rocky9.pkr.hcl +164 -0
- package/packer/images/Rocky9Arm64/Makefile +69 -0
- package/packer/images/Rocky9Arm64/README.md +122 -0
- package/packer/images/Rocky9Arm64/http/rocky9.ks.pkrtpl.hcl +114 -0
- package/packer/images/Rocky9Arm64/rocky9.pkr.hcl +171 -0
- package/packer/scripts/fuse-nbd +64 -0
- package/packer/scripts/fuse-tar-root +63 -0
- package/proxy.yaml +189 -0
- package/pv-pvc.yaml +164 -0
- package/scripts/device-scan.sh +65 -0
- package/scripts/disk-clean.sh +182 -0
- package/scripts/disk-devices.sh +13 -0
- package/scripts/gen-fqdns.sh +14 -0
- package/scripts/gpu-diag.sh +19 -0
- package/scripts/ip-info.sh +118 -0
- package/scripts/ipxe-setup.sh +200 -0
- package/scripts/k3s-node-setup.sh +124 -0
- package/scripts/kubeadm-node-setup.sh +317 -0
- package/scripts/link-local-underpost-cli.sh +6 -0
- package/scripts/lxd-vm-setup.sh +208 -0
- package/scripts/maas-nat-firewalld.sh +145 -0
- package/scripts/maas-setup.sh +135 -0
- package/scripts/maas-upload-boot-resource.sh +183 -0
- package/scripts/nat-iptables.sh +111 -0
- package/scripts/nvim.sh +91 -0
- package/scripts/packer-init-vars-file.sh +40 -0
- package/scripts/packer-setup.sh +289 -0
- package/scripts/ports-ls.sh +33 -0
- package/scripts/quick-tftp.sh +19 -0
- package/scripts/rhel-grpc-setup.sh +56 -0
- package/scripts/rocky-kickstart.sh +986 -0
- package/scripts/rocky-pwa.sh +200 -0
- package/scripts/rocky-setup.sh +106 -0
- package/scripts/rpmfusion-ffmpeg-setup.sh +32 -0
- package/scripts/ssl.sh +170 -0
- package/scripts/test-monitor.sh +248 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +30 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +155 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +39 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +382 -0
- package/src/api/core/core.controller.js +8 -0
- package/src/api/core/core.model.js +11 -0
- package/src/api/core/core.router.js +23 -0
- package/src/api/core/core.service.js +30 -0
- package/src/api/crypto/crypto.controller.js +10 -0
- package/src/api/crypto/crypto.model.js +23 -0
- package/src/api/crypto/crypto.router.js +23 -0
- package/src/api/crypto/crypto.service.js +64 -0
- package/src/api/cyberia-action/cyberia-action.controller.js +8 -0
- package/src/api/cyberia-action/cyberia-action.model.js +79 -0
- package/src/api/cyberia-action/cyberia-action.router.js +21 -0
- package/src/api/cyberia-action/cyberia-action.service.js +58 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +27 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +100 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +70 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +8 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +20 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +57 -0
- package/src/api/cyberia-entity/cyberia-entity.controller.js +6 -0
- package/src/api/cyberia-entity/cyberia-entity.model.js +25 -0
- package/src/api/cyberia-entity/cyberia-entity.router.js +17 -0
- package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +6 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +17 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +337 -0
- package/src/api/cyberia-instance/cyberia-instance-boot.service.js +57 -0
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +583 -0
- package/src/api/cyberia-instance/cyberia-instance.controller.js +42 -0
- package/src/api/cyberia-instance/cyberia-instance.model.js +117 -0
- package/src/api/cyberia-instance/cyberia-instance.router.js +75 -0
- package/src/api/cyberia-instance/cyberia-instance.service.js +190 -0
- package/src/api/cyberia-instance/cyberia-portal-connector.js +260 -0
- package/src/api/cyberia-instance/cyberia-world-generator.js +618 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +6 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +200 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +17 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +46 -0
- package/src/api/cyberia-map/cyberia-map.controller.js +6 -0
- package/src/api/cyberia-map/cyberia-map.model.js +33 -0
- package/src/api/cyberia-map/cyberia-map.router.js +19 -0
- package/src/api/cyberia-map/cyberia-map.service.js +76 -0
- package/src/api/cyberia-quest/cyberia-quest.controller.js +9 -0
- package/src/api/cyberia-quest/cyberia-quest.model.js +67 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +26 -0
- package/src/api/cyberia-quest/cyberia-quest.service.js +97 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +6 -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 +21 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +42 -0
- package/src/api/cyberia-saga/cyberia-saga.controller.js +6 -0
- package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
- package/src/api/cyberia-saga/cyberia-saga.router.js +17 -0
- package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +1076 -0
- package/src/api/cyberia-skill/cyberia-skill.controller.js +6 -0
- package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
- package/src/api/cyberia-skill/cyberia-skill.router.js +17 -0
- package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
- package/src/api/default/default.controller.js +6 -0
- package/src/api/default/default.model.js +20 -0
- package/src/api/default/default.router.js +21 -0
- package/src/api/default/default.service.js +42 -0
- package/src/api/document/document.controller.js +9 -0
- package/src/api/document/document.model.js +95 -0
- package/src/api/document/document.router.js +32 -0
- package/src/api/document/document.service.js +633 -0
- package/src/api/file/file.controller.js +15 -0
- package/src/api/file/file.model.js +127 -0
- package/src/api/file/file.ref.json +39 -0
- package/src/api/file/file.router.js +36 -0
- package/src/api/file/file.service.js +499 -0
- package/src/api/instance/instance.controller.js +8 -0
- package/src/api/instance/instance.model.js +34 -0
- package/src/api/instance/instance.router.js +27 -0
- package/src/api/instance/instance.service.js +75 -0
- package/src/api/ipfs/ipfs.controller.js +8 -0
- package/src/api/ipfs/ipfs.model.js +77 -0
- package/src/api/ipfs/ipfs.router.js +26 -0
- package/src/api/ipfs/ipfs.service.js +145 -0
- package/src/api/object-layer/object-layer.controller.js +18 -0
- package/src/api/object-layer/object-layer.model.js +200 -0
- package/src/api/object-layer/object-layer.router.js +729 -0
- package/src/api/object-layer/object-layer.service.js +894 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +6 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +93 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +17 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +46 -0
- package/src/api/test/test.controller.js +14 -0
- package/src/api/test/test.model.js +14 -0
- package/src/api/test/test.router.js +23 -0
- package/src/api/test/test.service.js +35 -0
- package/src/api/types.js +24 -0
- package/src/api/user/guest.service.js +100 -0
- package/src/api/user/postman_collection.json +216 -0
- package/src/api/user/user.build.js +16 -0
- package/src/api/user/user.controller.js +35 -0
- package/src/api/user/user.model.js +142 -0
- package/src/api/user/user.router.js +367 -0
- package/src/api/user/user.service.js +582 -0
- package/src/api.js +23 -0
- package/src/cli/baremetal.js +3947 -0
- package/src/cli/cloud-init.js +673 -0
- package/src/cli/cluster.js +1395 -0
- package/src/cli/db.js +1533 -0
- package/src/cli/deploy.js +1542 -0
- package/src/cli/docker-compose.js +802 -0
- package/src/cli/env.js +177 -0
- package/src/cli/fs.js +380 -0
- package/src/cli/image.js +389 -0
- package/src/cli/index.js +1059 -0
- package/src/cli/ipfs.js +182 -0
- package/src/cli/kickstart.js +271 -0
- package/src/cli/kubectl.js +214 -0
- package/src/cli/lxd.js +1232 -0
- package/src/cli/monitor.js +659 -0
- package/src/cli/release.js +595 -0
- package/src/cli/repository.js +1969 -0
- package/src/cli/run.js +3326 -0
- package/src/cli/secrets.js +151 -0
- package/src/cli/ssh.js +975 -0
- package/src/cli/static.js +653 -0
- package/src/cli/system.js +332 -0
- package/src/cli/test.js +176 -0
- package/src/client/Cryptokoyn.index.js +30 -0
- package/src/client/CyberiaPortal.index.js +31 -0
- package/src/client/Default.index.js +77 -0
- package/src/client/Itemledger.index.js +35 -0
- package/src/client/Underpost.index.js +31 -0
- package/src/client/components/core/404.js +20 -0
- package/src/client/components/core/500.js +20 -0
- package/src/client/components/core/Account.js +630 -0
- package/src/client/components/core/AgGrid.js +238 -0
- package/src/client/components/core/Alert.js +76 -0
- package/src/client/components/core/AppStore.js +69 -0
- package/src/client/components/core/Auth.js +353 -0
- package/src/client/components/core/Badge.js +26 -0
- package/src/client/components/core/Blockchain.js +41 -0
- package/src/client/components/core/Blog.js +9 -0
- package/src/client/components/core/BtnIcon.js +109 -0
- package/src/client/components/core/CalendarCore.js +437 -0
- package/src/client/components/core/Chat.js +62 -0
- package/src/client/components/core/ClientEvents.js +163 -0
- package/src/client/components/core/ColorPaletteElement.js +309 -0
- package/src/client/components/core/CommonJs.js +1014 -0
- package/src/client/components/core/Content.js +349 -0
- package/src/client/components/core/Css.js +1134 -0
- package/src/client/components/core/CssCore.js +881 -0
- package/src/client/components/core/D3Chart.js +44 -0
- package/src/client/components/core/Docs.js +478 -0
- package/src/client/components/core/DropDown.js +321 -0
- package/src/client/components/core/EventBus.js +96 -0
- package/src/client/components/core/EventsUI.js +130 -0
- package/src/client/components/core/FileExplorer.js +1406 -0
- package/src/client/components/core/FullScreen.js +201 -0
- package/src/client/components/core/Input.js +537 -0
- package/src/client/components/core/Keyboard.js +80 -0
- package/src/client/components/core/KeyboardAvoidance.js +145 -0
- package/src/client/components/core/LoadingAnimation.js +152 -0
- package/src/client/components/core/LogIn.js +212 -0
- package/src/client/components/core/LogOut.js +72 -0
- package/src/client/components/core/Logger.js +22 -0
- package/src/client/components/core/Modal.js +2956 -0
- package/src/client/components/core/NotificationManager.js +80 -0
- package/src/client/components/core/Pagination.js +252 -0
- package/src/client/components/core/Panel.js +1308 -0
- package/src/client/components/core/PanelForm.js +915 -0
- package/src/client/components/core/Polyhedron.js +947 -0
- package/src/client/components/core/PublicProfile.js +895 -0
- package/src/client/components/core/Recover.js +211 -0
- package/src/client/components/core/Responsive.js +146 -0
- package/src/client/components/core/RichText.js +33 -0
- package/src/client/components/core/Router.js +523 -0
- package/src/client/components/core/Scroll.js +76 -0
- package/src/client/components/core/SearchBox.js +1007 -0
- package/src/client/components/core/SignUp.js +153 -0
- package/src/client/components/core/SocketIo.js +163 -0
- package/src/client/components/core/SocketIoHandler.js +75 -0
- package/src/client/components/core/Stream.js +161 -0
- package/src/client/components/core/ToggleSwitch.js +89 -0
- package/src/client/components/core/ToolTip.js +78 -0
- package/src/client/components/core/Translate.js +694 -0
- package/src/client/components/core/Validator.js +140 -0
- package/src/client/components/core/VanillaJs.js +485 -0
- package/src/client/components/core/Wallet.js +95 -0
- package/src/client/components/core/WebComponent.js +44 -0
- package/src/client/components/core/Webhook.js +58 -0
- package/src/client/components/core/Worker.js +313 -0
- package/src/client/components/core/windowGetDimensions.js +269 -0
- package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +352 -0
- package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
- package/src/client/components/cryptokoyn/CssCryptokoyn.js +212 -0
- package/src/client/components/cryptokoyn/LogInCryptokoyn.js +16 -0
- package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +12 -0
- package/src/client/components/cryptokoyn/RouterCryptokoyn.js +37 -0
- package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +16 -0
- package/src/client/components/cryptokoyn/SignUpCryptokoyn.js +11 -0
- package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +6 -0
- package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
- package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
- package/src/client/components/cyberia/InstanceEngineCyberia.js +1088 -0
- package/src/client/components/cyberia/InstanceSelectionView.js +900 -0
- package/src/client/components/cyberia/MapEngineCyberia.js +1976 -0
- package/src/client/components/cyberia/ObjectLayerEngine.js +1540 -0
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +2420 -0
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +1522 -0
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +528 -0
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +772 -0
- package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +254 -0
- package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +21 -0
- package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +16 -0
- package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +518 -0
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +72 -0
- package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +16 -0
- package/src/client/components/cyberia-portal/SignUpCyberiaPortal.js +13 -0
- package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +6 -0
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +48 -0
- package/src/client/components/default/AppShellDefault.js +765 -0
- package/src/client/components/default/AppStoreDefault.js +5 -0
- package/src/client/components/default/CommonDefault.js +29 -0
- package/src/client/components/default/CssDefault.js +27 -0
- package/src/client/components/default/LogInDefault.js +16 -0
- package/src/client/components/default/LogOutDefault.js +12 -0
- package/src/client/components/default/RouterDefault.js +47 -0
- package/src/client/components/default/SettingsDefault.js +16 -0
- package/src/client/components/default/SignUpDefault.js +11 -0
- package/src/client/components/default/SocketIoDefault.js +6 -0
- package/src/client/components/default/TranslateDefault.js +7 -0
- package/src/client/components/itemledger/AppShellItemledger.js +353 -0
- package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
- package/src/client/components/itemledger/CssItemledger.js +212 -0
- package/src/client/components/itemledger/LogInItemledger.js +17 -0
- package/src/client/components/itemledger/LogOutItemledger.js +12 -0
- package/src/client/components/itemledger/RouterItemledger.js +38 -0
- package/src/client/components/itemledger/SettingsItemledger.js +16 -0
- package/src/client/components/itemledger/SignUpItemledger.js +11 -0
- package/src/client/components/itemledger/SocketIoItemledger.js +6 -0
- package/src/client/components/itemledger/TranslateItemledger.js +7 -0
- package/src/client/components/underpost/AppShellUnderpost.js +691 -0
- package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
- package/src/client/components/underpost/CssUnderpost.js +281 -0
- package/src/client/components/underpost/CyberpunkBloggerUnderpost.js +879 -0
- package/src/client/components/underpost/DocumentSearchProvider.js +448 -0
- package/src/client/components/underpost/LabGalleryUnderpost.js +79 -0
- package/src/client/components/underpost/LogInUnderpost.js +25 -0
- package/src/client/components/underpost/LogOutUnderpost.js +17 -0
- package/src/client/components/underpost/RouterUnderpost.js +45 -0
- package/src/client/components/underpost/SettingsUnderpost.js +16 -0
- package/src/client/components/underpost/SignUpUnderpost.js +11 -0
- package/src/client/components/underpost/SocketIoUnderpost.js +6 -0
- package/src/client/components/underpost/TranslateUnderpost.js +10 -0
- package/src/client/public/cryptokoyn/assets/banner/koyn-social.png +0 -0
- package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
- package/src/client/public/cryptokoyn/assets/logo/cryptokoyn.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-144x144.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-192x192.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-256x256.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-36x36.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-48x48.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-72x72.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-96x96.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/favicon-16x16.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/favicon-32x32.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-144x144.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-150x150.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-310x150.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-310x310.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-70x70.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/safari-pinned-tab.svg +42 -0
- package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
- package/src/client/public/cryptokoyn/favicon.ico +0 -0
- package/src/client/public/cryptokoyn/microdata.json +85 -0
- package/src/client/public/cryptokoyn/site.webmanifest +57 -0
- package/src/client/public/cryptokoyn/sitemap +148 -0
- package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +307 -0
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +309 -0
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +204 -0
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +341 -0
- package/src/client/public/cyberia-docs/CYBERIA-LORE.md +84 -0
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +294 -0
- package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +242 -0
- package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +300 -0
- package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +309 -0
- package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +228 -0
- package/src/client/public/cyberia-docs/ROADMAP.md +240 -0
- package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +740 -0
- 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-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152-precomposed.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-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76-precomposed.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/dark.jpg +0 -0
- package/src/client/public/default/assets/background/dark.svg +557 -0
- package/src/client/public/default/assets/background/white.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/default/assets/logo/base-icon.png +0 -0
- package/src/client/public/default/assets/logo/underpost.gif +0 -0
- package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-default-avatar.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
- package/src/client/public/default/browserconfig.xml +12 -0
- 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 +69 -0
- 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/safari-pinned-tab.svg +24 -0
- package/src/client/public/default/site.webmanifest +69 -0
- package/src/client/public/default/sitemap +148 -0
- package/src/client/public/default/yandex-browser-50x50.png +0 -0
- package/src/client/public/default/yandex-browser-manifest.json +9 -0
- package/src/client/public/doc/favicon.ico +0 -0
- package/src/client/public/doc/sitemap +148 -0
- package/src/client/public/itemledger/android-chrome-144x144.png +0 -0
- package/src/client/public/itemledger/android-chrome-192x192.png +0 -0
- package/src/client/public/itemledger/android-chrome-256x256.png +0 -0
- package/src/client/public/itemledger/android-chrome-36x36.png +0 -0
- package/src/client/public/itemledger/android-chrome-384x384.png +0 -0
- package/src/client/public/itemledger/android-chrome-48x48.png +0 -0
- package/src/client/public/itemledger/android-chrome-512x512.png +0 -0
- package/src/client/public/itemledger/android-chrome-72x72.png +0 -0
- package/src/client/public/itemledger/android-chrome-96x96.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-1024x1024.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-167x167.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1125x2436.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1136x640.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1170x2532.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1179x2556.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1242x2208.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1242x2688.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1284x2778.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1290x2796.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1334x750.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1488x2266.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1536x2048.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1620x2160.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1640x2160.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1668x2224.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1668x2388.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1792x828.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2048x1536.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2048x2732.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2160x1620.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2160x1640.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2208x1242.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2224x1668.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2266x1488.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2388x1668.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2436x1125.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2532x1170.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2556x1179.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2688x1242.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2732x2048.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2778x1284.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2796x1290.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-640x1136.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-750x1334.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-828x1792.png +0 -0
- package/src/client/public/itemledger/assets/logo/base-icon.png +0 -0
- package/src/client/public/itemledger/browserconfig.xml +12 -0
- package/src/client/public/itemledger/favicon-16x16.png +0 -0
- package/src/client/public/itemledger/favicon-32x32.png +0 -0
- package/src/client/public/itemledger/favicon-48x48.png +0 -0
- package/src/client/public/itemledger/favicon.ico +0 -0
- package/src/client/public/itemledger/manifest.webmanifest +69 -0
- package/src/client/public/itemledger/microdata.json +71 -0
- package/src/client/public/itemledger/mstile-144x144.png +0 -0
- package/src/client/public/itemledger/mstile-150x150.png +0 -0
- package/src/client/public/itemledger/mstile-310x150.png +0 -0
- package/src/client/public/itemledger/mstile-310x310.png +0 -0
- package/src/client/public/itemledger/mstile-70x70.png +0 -0
- package/src/client/public/itemledger/sitemap +148 -0
- package/src/client/public/itemledger/yandex-browser-50x50.png +0 -0
- package/src/client/public/itemledger/yandex-browser-manifest.json +9 -0
- package/src/client/public/test/favicon.ico +0 -0
- package/src/client/public/test/sitemap +148 -0
- package/src/client/services/atlas-sprite-sheet/atlas-sprite-sheet.service.js +137 -0
- package/src/client/services/core/core.service.js +309 -0
- package/src/client/services/crypto/crypto.service.js +68 -0
- package/src/client/services/cyberia-action/cyberia-action.service.js +99 -0
- package/src/client/services/cyberia-client-hints/cyberia-client-hints.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-entity-type-default/cyberia-entity-type-default.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 +137 -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/cyberia-saga/cyberia-saga.service.js +99 -0
- package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
- package/src/client/services/default/default.management.js +1015 -0
- package/src/client/services/default/default.service.js +99 -0
- package/src/client/services/document/document.service.js +134 -0
- package/src/client/services/file/file.service.js +94 -0
- package/src/client/services/instance/instance.management.js +78 -0
- package/src/client/services/instance/instance.service.js +107 -0
- package/src/client/services/ipfs/ipfs.service.js +118 -0
- package/src/client/services/object-layer/object-layer.management.js +414 -0
- package/src/client/services/object-layer/object-layer.service.js +208 -0
- package/src/client/services/object-layer-render-frames/object-layer-render-frames.service.js +99 -0
- package/src/client/services/test/test.service.js +68 -0
- package/src/client/services/user/guest.service.js +86 -0
- package/src/client/services/user/user.management.js +51 -0
- package/src/client/services/user/user.service.js +106 -0
- package/src/client/ssr/RootDocument.js +237 -0
- package/src/client/ssr/body/404.js +77 -0
- package/src/client/ssr/body/500.js +76 -0
- package/src/client/ssr/body/CacheControl.js +117 -0
- package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +97 -0
- package/src/client/ssr/body/DefaultSplashScreen.js +97 -0
- package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +90 -0
- package/src/client/ssr/head/CryptokoynScripts.js +4 -0
- package/src/client/ssr/head/Css.js +241 -0
- package/src/client/ssr/head/CyberiaPortalScripts.js +6 -0
- package/src/client/ssr/head/DefaultScripts.js +6 -0
- package/src/client/ssr/head/ItemledgerScripts.js +4 -0
- package/src/client/ssr/head/Microdata.js +11 -0
- package/src/client/ssr/head/Production.js +1 -0
- package/src/client/ssr/head/Pwa.js +146 -0
- package/src/client/ssr/head/PwaItemledger.js +197 -0
- package/src/client/ssr/head/Seo.js +15 -0
- package/src/client/ssr/head/UnderpostScripts.js +6 -0
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +20 -0
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +16 -0
- package/src/client/ssr/views/Cyberia404.js +328 -0
- package/src/client/ssr/views/CyberiaServerMetrics.js +1070 -0
- package/src/client/ssr/views/Maintenance.js +64 -0
- package/src/client/ssr/views/NoNetworkConnection.js +68 -0
- package/src/client/ssr/views/Test.js +199 -0
- package/src/client/sw/core.sw.js +274 -0
- package/src/client-builder/client-build-docs.js +493 -0
- package/src/client-builder/client-build-live.js +100 -0
- package/src/client-builder/client-build.js +1092 -0
- package/src/client-builder/client-dev-server.js +88 -0
- package/src/client-builder/client-formatted.js +170 -0
- package/src/client-builder/client-icons.js +108 -0
- package/src/client-builder/ssr.js +124 -0
- package/src/client.build.js +17 -0
- package/src/client.dev.js +21 -0
- package/src/db/DataBaseProvider.js +213 -0
- package/src/db/mariadb/MariaDB.js +72 -0
- package/src/db/mongo/MongoBootstrap.js +657 -0
- package/src/db/mongo/MongooseDB.js +199 -0
- package/src/grpc/cyberia/grpc-server.js +162 -0
- package/src/index.js +357 -0
- package/src/mailer/EmailRender.js +116 -0
- package/src/mailer/MailerProvider.js +213 -0
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +413 -0
- package/src/projects/cyberia/besu-genesis-generator.js +1630 -0
- package/src/projects/cyberia/catalog-cyberia.js +90 -0
- package/src/projects/cyberia/gemini-client.js +175 -0
- package/src/projects/cyberia/generate-saga.js +2137 -0
- package/src/projects/cyberia/hot-reload-trigger.js +163 -0
- package/src/projects/cyberia/instance-data.js +719 -0
- package/src/projects/cyberia/ipfs-client.js +599 -0
- package/src/projects/cyberia/map-preview-generator.js +304 -0
- package/src/projects/cyberia/object-layer.js +978 -0
- package/src/projects/cyberia/semantic-layer-generator-floor.js +319 -0
- package/src/projects/cyberia/semantic-layer-generator-resource.js +259 -0
- package/src/projects/cyberia/semantic-layer-generator-skin.js +1164 -0
- package/src/projects/cyberia/semantic-layer-generator.js +753 -0
- package/src/projects/cyberia/shape-generator.js +1060 -0
- package/src/projects/underpost/catalog-underpost.js +60 -0
- package/src/proxy.js +19 -0
- package/src/runtime/cyberia-client/Dockerfile +49 -0
- package/src/runtime/cyberia-client/Dockerfile.dev +75 -0
- package/src/runtime/cyberia-server/Dockerfile +43 -0
- package/src/runtime/cyberia-server/Dockerfile.dev +56 -0
- package/src/runtime/engine-cyberia/Dockerfile +161 -0
- package/src/runtime/engine-cyberia/Dockerfile.dev +161 -0
- package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
- package/src/runtime/engine-cyberia/compose.env +144 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +434 -0
- package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
- package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
- package/src/runtime/engine-cyberia/nginx.conf +128 -0
- package/src/runtime/express/Dockerfile +45 -0
- package/src/runtime/express/Express.js +285 -0
- package/src/runtime/lampp/Dockerfile +62 -0
- package/src/runtime/lampp/Lampp.js +359 -0
- package/src/runtime/nginx/Nginx.js +250 -0
- package/src/runtime/wp/Dockerfile +62 -0
- package/src/runtime/wp/Wp.js +642 -0
- package/src/server/auth.js +713 -0
- package/src/server/backup.js +102 -0
- package/src/server/catalog.js +72 -0
- package/src/server/conf.js +2316 -0
- package/src/server/cron.js +476 -0
- package/src/server/crypto.js +210 -0
- package/src/server/data-query.js +461 -0
- package/src/server/dns.js +553 -0
- package/src/server/downloader.js +73 -0
- package/src/server/logger.js +229 -0
- package/src/server/middlewares.js +152 -0
- package/src/server/peer.js +75 -0
- package/src/server/process.js +299 -0
- package/src/server/proxy.js +139 -0
- package/src/server/runtime-status.js +252 -0
- package/src/server/runtime.js +183 -0
- package/src/server/start.js +272 -0
- package/src/server/tls.js +254 -0
- package/src/server/valkey.js +201 -0
- package/src/server.js +25 -0
- package/src/ws/IoInterface.js +129 -0
- package/src/ws/IoServer.js +68 -0
- package/src/ws/core/channels/core.ws.chat.js +68 -0
- package/src/ws/core/channels/core.ws.mailer.js +116 -0
- package/src/ws/core/channels/core.ws.stream.js +90 -0
- package/src/ws/core/core.ws.connection.js +41 -0
- package/src/ws/core/core.ws.emit.js +37 -0
- package/src/ws/core/core.ws.server.js +44 -0
- package/src/ws/default/channels/default.ws.main.js +57 -0
- package/src/ws/default/default.ws.connection.js +35 -0
- package/src/ws/default/default.ws.emit.js +14 -0
- package/src/ws/default/default.ws.server.js +39 -0
- package/test/api.test.js +53 -0
- package/test/crypto.test.js +117 -0
- package/test/cyberia-instance-conf-defaults.test.js +140 -0
- package/test/deploy-monitor.test.js +267 -0
- package/test/shape-generator.test.js +463 -0
- package/tsconfig.docs.json +15 -0
- package/typedoc.dd-cyberia.json +31 -0
- package/typedoc.json +31 -0
|
@@ -0,0 +1,1969 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository module for managing Git operations and configurations.
|
|
3
|
+
* @module src/cli/repository.js
|
|
4
|
+
* @namespace UnderpostRepository
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import dotenv from 'dotenv';
|
|
8
|
+
import { commitData } from '../client/components/core/CommonJs.js';
|
|
9
|
+
import { pbcopy, shellCd, shellExec } from '../server/process.js';
|
|
10
|
+
import { actionInitLog, loggerFactory } from '../server/logger.js';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import fs from 'fs-extra';
|
|
13
|
+
import {
|
|
14
|
+
getNpmRootPath,
|
|
15
|
+
Config,
|
|
16
|
+
loadConf,
|
|
17
|
+
readConfJson,
|
|
18
|
+
getConfFilePath,
|
|
19
|
+
loadReplicas,
|
|
20
|
+
loadConfServerJson,
|
|
21
|
+
getDataDeploy,
|
|
22
|
+
buildReplicaId,
|
|
23
|
+
writeEnv,
|
|
24
|
+
readConfInstances,
|
|
25
|
+
} from '../server/conf.js';
|
|
26
|
+
import { buildClient, unzipClientBuild, mergeClientBuildZip } from '../client-builder/client-build.js';
|
|
27
|
+
import { DefaultConf } from '../../conf.js';
|
|
28
|
+
import Underpost from '../index.js';
|
|
29
|
+
|
|
30
|
+
const logger = loggerFactory(import.meta);
|
|
31
|
+
|
|
32
|
+
const diffCmd = `--no-pager show -U0 -w --word-diff=color --word-diff-regex='[^[:space:]]' --color=always`;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @class UnderpostRepository
|
|
36
|
+
* @description Manages Git operations and configurations.
|
|
37
|
+
* This class provides a set of static methods to automate various
|
|
38
|
+
* Git operations, including cloning, pulling, and committing changes.
|
|
39
|
+
* @memberof UnderpostRepository
|
|
40
|
+
*/
|
|
41
|
+
class UnderpostRepository {
|
|
42
|
+
static API = {
|
|
43
|
+
/**
|
|
44
|
+
* Clones a Git repository from GitHub.
|
|
45
|
+
* @param {string} [gitUri=`${process.env.GITHUB_USERNAME}/pwa-microservices-template`] - The URI of the GitHub repository (e.g., "username/repository").
|
|
46
|
+
* @param {object} [options={ bare: false, G8: false }] - Cloning options.
|
|
47
|
+
* @param {boolean} [options.bare=false] - If true, performs a bare clone.
|
|
48
|
+
* @param {boolean} [options.g8=false] - If true, uses the .g8 extension.
|
|
49
|
+
* @memberof UnderpostRepository
|
|
50
|
+
*/
|
|
51
|
+
clone(gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`, options = { bare: false, g8: false }) {
|
|
52
|
+
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
53
|
+
const repoName = gitUri.split('/').pop();
|
|
54
|
+
if (fs.existsSync(`./${repoName}`)) fs.removeSync(`./${repoName}`);
|
|
55
|
+
shellExec(
|
|
56
|
+
`git clone ${options?.bare === true ? ` --bare ` : ''}https://${
|
|
57
|
+
process.env.GITHUB_TOKEN ? `${process.env.GITHUB_TOKEN}@` : ''
|
|
58
|
+
}github.com/${gitUri}${gExtension}`,
|
|
59
|
+
{
|
|
60
|
+
disableLog: true,
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
},
|
|
64
|
+
/**
|
|
65
|
+
* Pulls updates from a GitHub repository.
|
|
66
|
+
* @param {string} [repoPath='./'] - The local path to the repository.
|
|
67
|
+
* @param {string} [gitUri=`${process.env.GITHUB_USERNAME}/pwa-microservices-template`] - The URI of the GitHub repository.
|
|
68
|
+
* @param {object} [options={ g8: false }] - Pulling options.
|
|
69
|
+
* @param {boolean} [options.g8=false] - If true, uses the .g8 extension.
|
|
70
|
+
* @memberof UnderpostRepository
|
|
71
|
+
*/
|
|
72
|
+
pull(
|
|
73
|
+
repoPath = './',
|
|
74
|
+
gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`,
|
|
75
|
+
options = { g8: false },
|
|
76
|
+
) {
|
|
77
|
+
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
78
|
+
shellExec(
|
|
79
|
+
`cd ${repoPath} && git pull https://${
|
|
80
|
+
process.env.GITHUB_TOKEN ? `${process.env.GITHUB_TOKEN}@` : ''
|
|
81
|
+
}github.com/${gitUri}${gExtension}`,
|
|
82
|
+
{
|
|
83
|
+
disableLog: true,
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
/**
|
|
88
|
+
* Creates a Git commit with a conventional commit message.
|
|
89
|
+
* @param {string} [repoPath='./'] - The local path to the repository.
|
|
90
|
+
* @param {string} [commitType='feat'] - The type of commit (e.g., 'feat', 'fix', 'docs', 'reset').
|
|
91
|
+
* @param {string} [subModule=''] - The submodule or scope of the commit.
|
|
92
|
+
* @param {string} [message=''] - The commit message.
|
|
93
|
+
* @param {object} [options={ copy: false, info: false, empty: false }] - Commit options.
|
|
94
|
+
* @param {boolean} [options.copy=false] - If true, copies the commit message to the clipboard.
|
|
95
|
+
* @param {boolean} [options.info=false] - If true, displays information about commit types.
|
|
96
|
+
* @param {boolean} [options.empty=false] - If true, allows an empty commit.
|
|
97
|
+
* @param {boolean} [options.diff=false] - If true, shows the diff of the last commit.
|
|
98
|
+
* @param {boolean} [options.edit=false] - If true, amends the last commit without changing the message.
|
|
99
|
+
* @param {boolean} [options.cached=false] - If true, commits only staged changes.
|
|
100
|
+
* @param {number} [options.log=0] - If greater than 0, shows the last N commits with diffs.
|
|
101
|
+
* @param {boolean} [options.lastMsg=0] - If greater than 0, copies or show the last last single n commit message to clipboard.
|
|
102
|
+
* @param {boolean} [options.unpush=false] - If true with --log, automatically detects unpushed commits ahead of remote and uses that count.
|
|
103
|
+
* @param {string} [options.deployId=''] - An optional deploy ID to include in the commit message.
|
|
104
|
+
* @param {string} [options.hashes=''] - If provided with diff option, shows the diff between two hashes.
|
|
105
|
+
* @param {string} [options.extension=''] - If provided with diff option, filters the diff by this file extension.
|
|
106
|
+
* @param {boolean|string} [options.changelog=undefined] - If true, prints the changelog since the last CI integration commit (starting with 'ci(package-pwa-microservices-'). If a number string, prints the changelog of the last N commits split by version sections. Only considers commits starting with '[<tag>]'.
|
|
107
|
+
* @param {boolean} [options.changelogBuild=false] - If true, scrapes all git history and builds a full CHANGELOG.md. Commits containing 'New release v:' are used as version section titles. Only commits starting with '[<tag>]' are included as entries.
|
|
108
|
+
* @param {string} [options.changelogMinVersion=''] - If set, overrides the default minimum version limit (2.85.0) for --changelog-build.
|
|
109
|
+
* @param {boolean} [options.changelogNoHash=false] - If true, omits commit hashes from the changelog entries.
|
|
110
|
+
* @param {boolean} [options.remoteUrl=false] - If true, prints the current git remote URL (origin) in plain text and returns.
|
|
111
|
+
* @param {string} [options.switchRepo=''] - If set, switches the remote `origin` to this URL and force-pulls the target branch, overwriting the current working tree.
|
|
112
|
+
* @param {string} [options.targetBranch=''] - Target branch for `switchRepo` (defaults to `master`).
|
|
113
|
+
* @memberof UnderpostRepository
|
|
114
|
+
*/
|
|
115
|
+
commit(
|
|
116
|
+
repoPath = undefined,
|
|
117
|
+
commitType = 'feat',
|
|
118
|
+
subModule = '',
|
|
119
|
+
message = '',
|
|
120
|
+
options = {
|
|
121
|
+
copy: false,
|
|
122
|
+
info: false,
|
|
123
|
+
empty: false,
|
|
124
|
+
diff: false,
|
|
125
|
+
edit: false,
|
|
126
|
+
cached: false,
|
|
127
|
+
lastMsg: 0,
|
|
128
|
+
log: 0,
|
|
129
|
+
deployId: '',
|
|
130
|
+
hashes: '',
|
|
131
|
+
extension: '',
|
|
132
|
+
changelog: undefined,
|
|
133
|
+
changelogBuild: false,
|
|
134
|
+
changelogMinVersion: '',
|
|
135
|
+
changelogNoHash: false,
|
|
136
|
+
unpush: false,
|
|
137
|
+
b: false,
|
|
138
|
+
p: undefined,
|
|
139
|
+
bc: '',
|
|
140
|
+
isRemoteRepo: '',
|
|
141
|
+
hasChanges: false,
|
|
142
|
+
remoteUrl: false,
|
|
143
|
+
switchRepo: '',
|
|
144
|
+
targetBranch: '',
|
|
145
|
+
},
|
|
146
|
+
) {
|
|
147
|
+
if (!repoPath) repoPath = '.';
|
|
148
|
+
|
|
149
|
+
if (options.initRepo) {
|
|
150
|
+
Underpost.repo.initLocalRepo({
|
|
151
|
+
path: repoPath,
|
|
152
|
+
origin: typeof options.initRepo === 'string' ? options.initRepo : undefined,
|
|
153
|
+
});
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (options.hasChanges) {
|
|
158
|
+
const status = shellExec(`cd ${repoPath} && git status --porcelain`, {
|
|
159
|
+
stdout: true,
|
|
160
|
+
silent: true,
|
|
161
|
+
disableLog: true,
|
|
162
|
+
}).trim();
|
|
163
|
+
process.stdout.write(status ? '1' : '');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (options.isRemoteRepo) {
|
|
168
|
+
const accessible = Underpost.repo.isRemoteRepo(options.isRemoteRepo);
|
|
169
|
+
console.log(accessible);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (options.remoteUrl) {
|
|
174
|
+
const url = Underpost.repo.getRemoteUrl({ path: repoPath });
|
|
175
|
+
if (options.copy) pbcopy(url);
|
|
176
|
+
else console.log(url);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (options.switchRepo) {
|
|
181
|
+
Underpost.repo.switchRemote({
|
|
182
|
+
path: repoPath,
|
|
183
|
+
url: options.switchRepo,
|
|
184
|
+
branch: options.targetBranch || 'master',
|
|
185
|
+
});
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (options.bc) {
|
|
190
|
+
console.log(
|
|
191
|
+
shellExec(`cd ${repoPath} && git for-each-ref --contains ${options.bc} --format='%(refname:short)'`, {
|
|
192
|
+
stdout: true,
|
|
193
|
+
silent: true,
|
|
194
|
+
disableLog: true,
|
|
195
|
+
}).trim(),
|
|
196
|
+
);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (options.p !== undefined) {
|
|
201
|
+
const branch =
|
|
202
|
+
options.p === true
|
|
203
|
+
? shellExec(`cd ${repoPath} && git branch --show-current`, {
|
|
204
|
+
stdout: true,
|
|
205
|
+
silent: true,
|
|
206
|
+
disableLog: true,
|
|
207
|
+
}).trim()
|
|
208
|
+
: options.p;
|
|
209
|
+
console.log(
|
|
210
|
+
shellExec(`cd ${repoPath} && git --no-pager reflog show refs/heads/${branch}`, {
|
|
211
|
+
stdout: true,
|
|
212
|
+
silent: true,
|
|
213
|
+
disableLog: true,
|
|
214
|
+
}).trim(),
|
|
215
|
+
);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (options.b) {
|
|
220
|
+
const currentBranch = shellExec(`cd ${repoPath} && git branch --show-current`, {
|
|
221
|
+
stdout: true,
|
|
222
|
+
silent: true,
|
|
223
|
+
disableLog: true,
|
|
224
|
+
}).trim();
|
|
225
|
+
if (options.copy) pbcopy(currentBranch);
|
|
226
|
+
else console.log(currentBranch);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (options.changelog !== undefined || options.changelogBuild || options.changelogMsg !== undefined) {
|
|
231
|
+
const releaseMatch = 'New release v:';
|
|
232
|
+
// Helper: parse [<tag>] commits into grouped sections
|
|
233
|
+
const buildSectionChangelog = (commits) => {
|
|
234
|
+
const groups = {};
|
|
235
|
+
const tagOrder = [];
|
|
236
|
+
for (const commit of commits) {
|
|
237
|
+
if (!commit.message.startsWith('[')) continue;
|
|
238
|
+
const match = commit.message.match(/^\[([^\]]+)\]\s*(.*)/);
|
|
239
|
+
if (match) {
|
|
240
|
+
const tag = match[1].trim();
|
|
241
|
+
const context = match[2].trim().replaceAll('"', '');
|
|
242
|
+
if (!groups[tag]) {
|
|
243
|
+
groups[tag] = [];
|
|
244
|
+
tagOrder.push(tag);
|
|
245
|
+
}
|
|
246
|
+
groups[tag].push({ ...commit, context });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
let out = '';
|
|
250
|
+
for (const tag of tagOrder) {
|
|
251
|
+
out += `### ${tag}\n\n`;
|
|
252
|
+
for (const entry of groups[tag]) {
|
|
253
|
+
out += `- ${entry.context}${options.changelogNoHash ? '' : ` (${commitUrl(entry.hash, entry.fullHash)})`}\n`;
|
|
254
|
+
}
|
|
255
|
+
out += '\n';
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// Helper: fetch git log as structured array
|
|
261
|
+
const fetchHistory = (limit) => {
|
|
262
|
+
const limitArg = limit ? ` -n ${limit}` : '';
|
|
263
|
+
const rawLog = shellExec(`git log --pretty=format:"%h||%H||%s||%ci"${limitArg}`, {
|
|
264
|
+
stdout: true,
|
|
265
|
+
silent: true,
|
|
266
|
+
disableLog: true,
|
|
267
|
+
silentOnError: true,
|
|
268
|
+
}).toString();
|
|
269
|
+
return rawLog
|
|
270
|
+
.split('\n')
|
|
271
|
+
.map((line) => {
|
|
272
|
+
const parts = line.split('||');
|
|
273
|
+
return {
|
|
274
|
+
hash: (parts[0] || '').trim(),
|
|
275
|
+
fullHash: (parts[1] || '').trim(),
|
|
276
|
+
message: parts[2] || '',
|
|
277
|
+
date: parts[3] || '',
|
|
278
|
+
};
|
|
279
|
+
})
|
|
280
|
+
.filter((c) => c.hash);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const githubUser = process.env.GITHUB_USERNAME || 'underpostnet';
|
|
284
|
+
const commitUrl = (shortHash, fullHash) =>
|
|
285
|
+
`[${shortHash}](https://github.com/${githubUser}/engine/commit/${fullHash})`;
|
|
286
|
+
|
|
287
|
+
// Helper: extract version from commit message containing 'New release v:'
|
|
288
|
+
const extractVersion = (message) => {
|
|
289
|
+
const idx = message.indexOf(releaseMatch);
|
|
290
|
+
if (idx === -1) return null;
|
|
291
|
+
return message.substring(idx + releaseMatch.length).trim();
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// Helper: split commits array into version sections by 'New release v:' boundary
|
|
295
|
+
const buildVersionSections = (commits) => {
|
|
296
|
+
const sections = [];
|
|
297
|
+
let currentSection = { title: null, date: new Date().toISOString().split('T')[0], commits: [] };
|
|
298
|
+
|
|
299
|
+
for (const commit of commits) {
|
|
300
|
+
const version = extractVersion(commit.message);
|
|
301
|
+
if (version) {
|
|
302
|
+
// Push accumulated commits as a section
|
|
303
|
+
sections.push(currentSection);
|
|
304
|
+
// Start new version section; commits below this one belong to it
|
|
305
|
+
const commitDate = commit.date ? commit.date.split(' ')[0] : '';
|
|
306
|
+
currentSection = { title: `${releaseMatch}${version}`, date: commitDate, hash: commit.hash, commits: [] };
|
|
307
|
+
} else {
|
|
308
|
+
currentSection.commits.push(commit);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Push the last (oldest) section
|
|
312
|
+
if (currentSection.commits.length > 0) sections.push(currentSection);
|
|
313
|
+
return sections;
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// Helper: render sections array into changelog markdown string
|
|
317
|
+
const renderSections = (sections) => {
|
|
318
|
+
let changelog = '';
|
|
319
|
+
for (const section of sections) {
|
|
320
|
+
const sectionBody = buildSectionChangelog(section.commits);
|
|
321
|
+
if (!sectionBody) continue;
|
|
322
|
+
if (section.title) {
|
|
323
|
+
changelog += `## ${section.title}${options.changelogNoHash ? '' : ` (${section.date})`}\n\n`;
|
|
324
|
+
} else {
|
|
325
|
+
changelog += `## ${section.date}\n\n`;
|
|
326
|
+
}
|
|
327
|
+
changelog += sectionBody;
|
|
328
|
+
}
|
|
329
|
+
return changelog;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const changelogMinVersion = options.changelogMinVersion || '2.97.1';
|
|
333
|
+
|
|
334
|
+
if (options.changelogBuild) {
|
|
335
|
+
// --changelog-build: scrape ALL history, split by 'New release v:' commits as version sections
|
|
336
|
+
const allCommits = fetchHistory();
|
|
337
|
+
const sections = buildVersionSections(allCommits);
|
|
338
|
+
|
|
339
|
+
// Filter sections: stop at changelogMinVersion boundary
|
|
340
|
+
const limitedSections = [];
|
|
341
|
+
for (const section of sections) {
|
|
342
|
+
limitedSections.push(section);
|
|
343
|
+
if (section.title) {
|
|
344
|
+
const versionStr = section.title.replace(releaseMatch, '').trim();
|
|
345
|
+
if (versionStr === changelogMinVersion) break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
let changelog = renderSections(limitedSections);
|
|
350
|
+
|
|
351
|
+
if (!changelog) {
|
|
352
|
+
changelog = `No changelog entries found.\n`;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const changelogPath = `${repoPath === '.' ? '.' : repoPath}/CHANGELOG.md`;
|
|
356
|
+
fs.writeFileSync(changelogPath, `# Changelog\n\n${changelog}`);
|
|
357
|
+
logger.info('CHANGELOG.md built at', changelogPath);
|
|
358
|
+
} else {
|
|
359
|
+
// --changelog / --changelog-msg: message from the last N commits, where N is --from-n-commit
|
|
360
|
+
// (default 1, last commit only). No auto-detection.
|
|
361
|
+
const n = parseInt(options.fromNCommit) > 0 ? parseInt(options.fromNCommit) : 1;
|
|
362
|
+
const sections = buildVersionSections(fetchHistory(n));
|
|
363
|
+
const changelog = renderSections(sections);
|
|
364
|
+
if (options.changelogMsg !== undefined) {
|
|
365
|
+
// Sanitized, commit-ready message; empty string when there are no tagged entries so
|
|
366
|
+
// callers fall back to their own generic default instead of a placeholder.
|
|
367
|
+
console.log(Underpost.repo.sanitizeChangelogMessage(changelog));
|
|
368
|
+
} else {
|
|
369
|
+
console.log(changelog || `No changelog entries found.\n`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
if (options.diff && options.hashes) {
|
|
376
|
+
const hashes = options.hashes.split(',');
|
|
377
|
+
const cmd = `git --no-pager diff ${hashes[0]} ${hashes[1] ? hashes[1] : 'HEAD'}${options.extension ? ` -- '*.${options.extension}'` : ''}`;
|
|
378
|
+
if (options.copy) {
|
|
379
|
+
pbcopy(cmd);
|
|
380
|
+
} else console.log(cmd);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (options.lastMsg) {
|
|
384
|
+
if (options.copy) {
|
|
385
|
+
pbcopy(Underpost.repo.getLastCommitMsg(options.lastMsg - 1));
|
|
386
|
+
} else console.log(Underpost.repo.getLastCommitMsg(options.lastMsg - 1));
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (options.diff) {
|
|
390
|
+
const _diffCmd = `git ${diffCmd.replace('show', `diff${options.cached ? ` --cached` : ''}`)}`;
|
|
391
|
+
if (options.copy) pbcopy(_diffCmd);
|
|
392
|
+
else console.log('Diff command:', _diffCmd);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (options.log || options.unpush) {
|
|
396
|
+
if (options.unpush) {
|
|
397
|
+
const { count, hasUnpushed } = Underpost.repo.getUnpushedCount(repoPath);
|
|
398
|
+
if (!hasUnpushed) {
|
|
399
|
+
logger.warn('No unpushed commits found');
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
options.log = count;
|
|
403
|
+
}
|
|
404
|
+
const history = Underpost.repo.getHistory(options.log, repoPath);
|
|
405
|
+
const chainCmd = history
|
|
406
|
+
.reverse()
|
|
407
|
+
.map((commitData, i) => `${i === 0 ? '' : ' && '}git -C ${repoPath} ${diffCmd} ${commitData.hash}`)
|
|
408
|
+
.join('');
|
|
409
|
+
if (history[0]) {
|
|
410
|
+
let index = history.length;
|
|
411
|
+
for (const commit of history) {
|
|
412
|
+
console.log(
|
|
413
|
+
shellExec(`cd ${repoPath} && git show -s --format=%ci ${commit.hash}`, {
|
|
414
|
+
stdout: true,
|
|
415
|
+
silent: true,
|
|
416
|
+
disableLog: true,
|
|
417
|
+
}).trim().green,
|
|
418
|
+
);
|
|
419
|
+
console.log(`${index}`.magenta, commit.hash.yellow, commit.message);
|
|
420
|
+
index--;
|
|
421
|
+
console.log(
|
|
422
|
+
shellExec(`cd ${repoPath} && git show --name-status --pretty="" ${commit.hash}`, {
|
|
423
|
+
stdout: true,
|
|
424
|
+
silent: true,
|
|
425
|
+
disableLog: true,
|
|
426
|
+
}).trim().red,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
if (options.copy) pbcopy(chainCmd);
|
|
430
|
+
else console.log('Show all:', chainCmd);
|
|
431
|
+
} else logger.warn('No commits found');
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (commitType === 'reset') {
|
|
435
|
+
if (options.copy) pbcopy(Underpost.repo.getLastCommitMsg());
|
|
436
|
+
shellExec(`cd ${repoPath} && git reset --soft HEAD~${isNaN(parseInt(subModule)) ? 1 : parseInt(subModule)}`);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
if (options.info) return logger.info('', commitData);
|
|
440
|
+
const _message = `${commitType}${subModule ? `(${subModule})` : ''}: ${
|
|
441
|
+
commitData[commitType].emoji
|
|
442
|
+
} ${message ? message : commitData[commitType].description}`;
|
|
443
|
+
if (options.copy) return pbcopy(_message);
|
|
444
|
+
shellExec(
|
|
445
|
+
`cd ${repoPath} && git commit ${options?.empty ? `--allow-empty ` : ''}${options.edit ? `--amend --no-edit ` : `-m "${_message}"`}`,
|
|
446
|
+
);
|
|
447
|
+
},
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Retrieves the message of the last Git commit.
|
|
451
|
+
* @param {number} [skip=0] - Number of commits to skip from HEAD (0 = most recent).
|
|
452
|
+
* @returns {string} The last commit message.
|
|
453
|
+
* @memberof UnderpostRepository
|
|
454
|
+
*/
|
|
455
|
+
getLastCommitMsg(skip = 0) {
|
|
456
|
+
return shellExec(`git --no-pager log -1 --skip=${skip} --pretty=%B`, {
|
|
457
|
+
stdout: true,
|
|
458
|
+
silent: true,
|
|
459
|
+
disableLog: true,
|
|
460
|
+
});
|
|
461
|
+
},
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Pushes commits to a remote GitHub repository.
|
|
465
|
+
* @param {string} [repoPath='./'] - The local path to the repository.
|
|
466
|
+
* @param {string} [gitUri=`${process.env.GITHUB_USERNAME}/pwa-microservices-template`] - The URI of the GitHub repository.
|
|
467
|
+
* @param {object} [options={ f: false, g8: false }] - Push options.
|
|
468
|
+
* @param {boolean} [options.f=false] - If true, forces the push.
|
|
469
|
+
* @param {boolean} [options.g8=false] - If true, uses the .g8 extension.
|
|
470
|
+
* @memberof UnderpostRepository
|
|
471
|
+
*/
|
|
472
|
+
push(
|
|
473
|
+
repoPath = './',
|
|
474
|
+
gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`,
|
|
475
|
+
options = { f: false, g8: false },
|
|
476
|
+
) {
|
|
477
|
+
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
478
|
+
shellExec(
|
|
479
|
+
`cd ${repoPath} && git push https://${process.env.GITHUB_TOKEN}@github.com/${gitUri}${gExtension}${
|
|
480
|
+
options?.f === true ? ' --force' : ''
|
|
481
|
+
}`,
|
|
482
|
+
{
|
|
483
|
+
disableLog: true,
|
|
484
|
+
},
|
|
485
|
+
);
|
|
486
|
+
logger.info(
|
|
487
|
+
'commit url',
|
|
488
|
+
`http://github.com/${gitUri}${gExtension === '.g8' ? '.g8' : ''}/commit/${shellExec(
|
|
489
|
+
`cd ${repoPath} && git rev-parse --verify HEAD`,
|
|
490
|
+
{
|
|
491
|
+
stdout: true,
|
|
492
|
+
},
|
|
493
|
+
).trim()}`,
|
|
494
|
+
);
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Initializes a new Underpost repository, optionally setting up a deploy ID or sub-configuration.
|
|
499
|
+
* @param {string} [projectName=''] - The name of the project to create.
|
|
500
|
+
* @param {object} [options] - Initialization options.
|
|
501
|
+
* @param {string} [options.deployId=''] - The deployment ID to set up.
|
|
502
|
+
* @param {string} [options.subConf=''] - The sub-configuration to create.
|
|
503
|
+
* @param {boolean} [options.cluster=false] - If true, sets up a clustered configuration.
|
|
504
|
+
* @param {boolean} [options.dev=false] - If true, uses development settings.
|
|
505
|
+
* @param {boolean} [options.buildRepos=false] - If true, creates the deployment repositories (engine-*, engine-*-private, engine-*-cron-backups).
|
|
506
|
+
* @param {boolean} [options.purge=false] - If true, removes the deploy ID conf and all related repositories (requires deployId).
|
|
507
|
+
* @param {boolean} [options.cleanTemplate=false] - If true, cleans the pwa-microservices-template build directory.
|
|
508
|
+
* @param {boolean} [options.build=false] - If true, builds the deployment to pwa-microservices-template (requires deployId).
|
|
509
|
+
* @param {boolean} [options.syncConf=false] - If true, syncs configuration to private repositories (requires deployId).
|
|
510
|
+
* @param {boolean} [options.syncStart=false] - If true, syncs start scripts in deploy ID package.json with root package.json.
|
|
511
|
+
* @param {boolean} [options.defaultConf=false] - If true, updates the default configuration file (requires deployId).
|
|
512
|
+
* @param {string} [options.confWorkflowId=''] - If provided, uses this configuration workflow ID.
|
|
513
|
+
* @returns {Promise<boolean>} A promise that resolves when the initialization is complete.
|
|
514
|
+
* @memberof UnderpostRepository
|
|
515
|
+
*/
|
|
516
|
+
new(
|
|
517
|
+
projectName,
|
|
518
|
+
options = {
|
|
519
|
+
deployId: '',
|
|
520
|
+
subConf: '',
|
|
521
|
+
cluster: false,
|
|
522
|
+
dev: false,
|
|
523
|
+
buildRepos: false,
|
|
524
|
+
purge: false,
|
|
525
|
+
cleanTemplate: false,
|
|
526
|
+
build: false,
|
|
527
|
+
syncConf: false,
|
|
528
|
+
syncStart: false,
|
|
529
|
+
defaultConf: false,
|
|
530
|
+
confWorkflowId: '',
|
|
531
|
+
},
|
|
532
|
+
) {
|
|
533
|
+
return new Promise(async (resolve, reject) => {
|
|
534
|
+
try {
|
|
535
|
+
await logger.setUpInfo();
|
|
536
|
+
actionInitLog();
|
|
537
|
+
|
|
538
|
+
// Handle cleanTemplate operation
|
|
539
|
+
if (options.cleanTemplate) {
|
|
540
|
+
logger.info('Cleaning build directory');
|
|
541
|
+
const basePath = '../pwa-microservices-template';
|
|
542
|
+
shellExec(`cd ${basePath} && git reset`);
|
|
543
|
+
shellExec(`cd ${basePath} && git checkout .`);
|
|
544
|
+
shellExec(`cd ${basePath} && git clean -f -d`);
|
|
545
|
+
logger.info('Build directory cleaned successfully');
|
|
546
|
+
return resolve(true);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Handle defaultConf operation
|
|
550
|
+
if (options.defaultConf) {
|
|
551
|
+
Underpost.repo.updateDefaultConf(options);
|
|
552
|
+
return resolve(true);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (options.deployId) {
|
|
556
|
+
let deployId = options.deployId;
|
|
557
|
+
|
|
558
|
+
// Handle sync-start operation (before dd- prefix normalization to support 'dd' special case)
|
|
559
|
+
if (options.syncStart) {
|
|
560
|
+
shellExec(`node bin/deploy sync-start ${deployId}`);
|
|
561
|
+
return resolve(true);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
|
|
565
|
+
// Handle purge operation
|
|
566
|
+
if (options.purge) {
|
|
567
|
+
logger.info(`Purging deploy ID: ${deployId}`);
|
|
568
|
+
|
|
569
|
+
const suffix = deployId.split('dd-')[1];
|
|
570
|
+
const repoName = `engine-${suffix}`;
|
|
571
|
+
const privateRepoName = `engine-${suffix}-private`;
|
|
572
|
+
const cronRepoName = `engine-${suffix}-cron-backups`;
|
|
573
|
+
const confFolder = `./engine-private/conf/${deployId}`;
|
|
574
|
+
|
|
575
|
+
// Remove conf folder
|
|
576
|
+
if (fs.existsSync(confFolder)) {
|
|
577
|
+
fs.removeSync(confFolder);
|
|
578
|
+
logger.info(`Removed conf folder: ${confFolder}`);
|
|
579
|
+
} else {
|
|
580
|
+
logger.warn(`Conf folder not found: ${confFolder}`);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Remove repositories
|
|
584
|
+
const repos = [
|
|
585
|
+
{ path: `../${repoName}`, name: repoName },
|
|
586
|
+
{ path: `../${privateRepoName}`, name: privateRepoName },
|
|
587
|
+
{ path: `../${cronRepoName}`, name: cronRepoName },
|
|
588
|
+
];
|
|
589
|
+
|
|
590
|
+
for (const repo of repos) {
|
|
591
|
+
if (fs.existsSync(repo.path)) {
|
|
592
|
+
fs.removeSync(repo.path);
|
|
593
|
+
logger.info(`Removed repository: ${repo.path}`);
|
|
594
|
+
} else {
|
|
595
|
+
logger.warn(`Repository not found: ${repo.path}`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
logger.info(`Successfully purged deploy ID: ${deployId}`);
|
|
600
|
+
return resolve(true);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Handle sync-conf operation
|
|
604
|
+
if (options.syncConf) {
|
|
605
|
+
logger.info(`Syncing configuration for deploy ID: ${deployId}`);
|
|
606
|
+
shellExec(`node bin/build ${deployId} --conf`);
|
|
607
|
+
logger.info('Configuration synced successfully');
|
|
608
|
+
return resolve(true);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Handle build operation
|
|
612
|
+
if (options.build) {
|
|
613
|
+
logger.info(`Building deployment for deploy ID: ${deployId}`);
|
|
614
|
+
shellExec(`node bin/build ${deployId}`);
|
|
615
|
+
logger.info('Build completed successfully');
|
|
616
|
+
return resolve(true);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Normal deploy ID factory operation
|
|
620
|
+
const { deployId: normalizedDeployId } = Config.deployIdFactory(deployId, options);
|
|
621
|
+
|
|
622
|
+
if (options.buildRepos) {
|
|
623
|
+
const suffix = normalizedDeployId.split('dd-')[1];
|
|
624
|
+
const repoName = `engine-${suffix}`;
|
|
625
|
+
const privateRepoName = `engine-${suffix}-private`;
|
|
626
|
+
const cronRepoName = `engine-${suffix}-cron-backups`;
|
|
627
|
+
const repos = [
|
|
628
|
+
{ path: `../${repoName}`, name: repoName },
|
|
629
|
+
{ path: `../${privateRepoName}`, name: privateRepoName },
|
|
630
|
+
{ path: `../${cronRepoName}`, name: cronRepoName },
|
|
631
|
+
];
|
|
632
|
+
|
|
633
|
+
const username = process.env.GITHUB_USERNAME;
|
|
634
|
+
const token = process.env.GITHUB_TOKEN;
|
|
635
|
+
|
|
636
|
+
if (!username) {
|
|
637
|
+
logger.error('GITHUB_USERNAME environment variable not set');
|
|
638
|
+
return reject(false);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
for (const repo of repos) {
|
|
642
|
+
if (!fs.existsSync(repo.path)) {
|
|
643
|
+
fs.mkdirSync(repo.path, { recursive: true });
|
|
644
|
+
logger.info(`Created repository directory: ${repo.path}`);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const remoteUrl = `https://${token ? `${token}@` : ''}github.com/${username}/${repo.name}.git`;
|
|
648
|
+
UnderpostRepository.API.initLocalRepo({ path: repo.path, origin: remoteUrl });
|
|
649
|
+
logger.info(`Initialized git repository with remote: ${repo.name}`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return resolve(true);
|
|
653
|
+
}
|
|
654
|
+
if (projectName) {
|
|
655
|
+
const npmRoot = getNpmRootPath();
|
|
656
|
+
const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
|
|
657
|
+
const destFolder = `./${projectName}`;
|
|
658
|
+
const deployId = projectName.startsWith('dd-') ? projectName : `dd-${projectName}`;
|
|
659
|
+
logger.info('build app', { destFolder, deployId });
|
|
660
|
+
if (fs.existsSync(destFolder)) fs.removeSync(destFolder);
|
|
661
|
+
fs.mkdirSync(destFolder, { recursive: true });
|
|
662
|
+
if (!options.dev) {
|
|
663
|
+
fs.copySync(underpostRoot, destFolder);
|
|
664
|
+
fs.writeFileSync(
|
|
665
|
+
`${destFolder}/.gitignore`,
|
|
666
|
+
fs.readFileSync(`${underpostRoot}/.dockerignore`, 'utf8'),
|
|
667
|
+
'utf8',
|
|
668
|
+
);
|
|
669
|
+
UnderpostRepository.API.initLocalRepo({ path: destFolder });
|
|
670
|
+
shellExec(`cd ${destFolder} && git add . && git commit -m "Base template implementation"`);
|
|
671
|
+
}
|
|
672
|
+
shellExec(`cd ${destFolder} && node bin new --deploy-id ${deployId} --default-conf`);
|
|
673
|
+
shellExec(`cd ${destFolder} && node bin client ${deployId}`);
|
|
674
|
+
shellExec(`cd ${destFolder} && DEPLOY_ID=${deployId} npm run dev`);
|
|
675
|
+
}
|
|
676
|
+
return resolve(true);
|
|
677
|
+
} catch (error) {
|
|
678
|
+
console.log(error);
|
|
679
|
+
logger.error(error, error.stack);
|
|
680
|
+
return reject(false);
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
},
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Builds client assets, single replicas, and/or syncs environment ports.
|
|
687
|
+
* @param {string} [deployId='dd-default'] - The deployment ID.
|
|
688
|
+
* @param {string} [subConf=''] - The sub-configuration for the build.
|
|
689
|
+
* @param {string} [host=''] - Comma-separated hosts to filter the build.
|
|
690
|
+
* @param {string} [path=''] - Comma-separated paths to filter the build.
|
|
691
|
+
* @param {object} [options] - Build options.
|
|
692
|
+
* @param {boolean} [options.syncEnvPort=false] - If true, syncs environment port assignments across all deploy IDs.
|
|
693
|
+
* @param {boolean} [options.singleReplica=false] - If true, builds single replica folders instead of full client.
|
|
694
|
+
* @param {boolean} [options.buildZip=false] - If true, creates zip files of the builds.
|
|
695
|
+
* @param {string|number} [options.split=''] - Optional ZIP part size in MB. When set with buildZip, writes split parts.
|
|
696
|
+
* @param {string} [options.unzip=''] - Optional build ZIP prefix to extract from ./build.
|
|
697
|
+
* @param {string} [options.mergeZip=''] - Optional build prefix to merge split ZIP parts into a single ZIP.
|
|
698
|
+
* @param {boolean} [options.liteBuild=false] - If true, skips full build (default is full build).
|
|
699
|
+
* @param {boolean} [options.iconsBuild=false] - If true, builds icons.
|
|
700
|
+
* @returns {Promise<boolean>} A promise that resolves when the build is complete.
|
|
701
|
+
* @memberof UnderpostRepository
|
|
702
|
+
*/
|
|
703
|
+
client(
|
|
704
|
+
deployId = 'dd-default',
|
|
705
|
+
subConf = '',
|
|
706
|
+
host = '',
|
|
707
|
+
path = '',
|
|
708
|
+
options = {
|
|
709
|
+
syncEnvPort: false,
|
|
710
|
+
singleReplica: false,
|
|
711
|
+
buildZip: false,
|
|
712
|
+
split: '',
|
|
713
|
+
unzip: '',
|
|
714
|
+
mergeZip: '',
|
|
715
|
+
liteBuild: false,
|
|
716
|
+
iconsBuild: false,
|
|
717
|
+
},
|
|
718
|
+
) {
|
|
719
|
+
return new Promise(async (resolve, reject) => {
|
|
720
|
+
try {
|
|
721
|
+
if (options.mergeZip) {
|
|
722
|
+
mergeClientBuildZip({
|
|
723
|
+
buildPrefix: options.mergeZip,
|
|
724
|
+
logger,
|
|
725
|
+
});
|
|
726
|
+
return resolve(true);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (options.unzip) {
|
|
730
|
+
unzipClientBuild({
|
|
731
|
+
buildPrefix: options.unzip,
|
|
732
|
+
logger,
|
|
733
|
+
});
|
|
734
|
+
return resolve(true);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Handle singleReplica operation (must run before syncEnvPort to ensure replica dirs exist)
|
|
738
|
+
if (options.singleReplica) {
|
|
739
|
+
const replicaPath = path;
|
|
740
|
+
if (!deployId || !host || !replicaPath) {
|
|
741
|
+
logger.error('client --single-replica requires deploy-id, host, and path arguments');
|
|
742
|
+
return reject(false);
|
|
743
|
+
}
|
|
744
|
+
const serverConf = loadReplicas(
|
|
745
|
+
deployId,
|
|
746
|
+
loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`),
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
if (serverConf[host][replicaPath].replicas) {
|
|
750
|
+
{
|
|
751
|
+
let replicaIndex = -1;
|
|
752
|
+
for (const replica of serverConf[host][replicaPath].replicas) {
|
|
753
|
+
replicaIndex++;
|
|
754
|
+
const replicaDeployId = `${deployId}-${serverConf[host][replicaPath].replicas[replicaIndex].slice(1)}`;
|
|
755
|
+
await fs.copy(`./engine-private/conf/${deployId}`, `./engine-private/replica/${replicaDeployId}`);
|
|
756
|
+
fs.writeFileSync(
|
|
757
|
+
`./engine-private/replica/${replicaDeployId}/package.json`,
|
|
758
|
+
fs
|
|
759
|
+
.readFileSync(`./engine-private/replica/${replicaDeployId}/package.json`, 'utf8')
|
|
760
|
+
.replaceAll(`${deployId}`, `${replicaDeployId}`),
|
|
761
|
+
'utf8',
|
|
762
|
+
);
|
|
763
|
+
const replicaFolder = `./engine-private/replica/${replicaDeployId}`;
|
|
764
|
+
for (const envFile of ['.env.production', '.env.development', '.env.test']) {
|
|
765
|
+
const envFilePath = `${replicaFolder}/${envFile}`;
|
|
766
|
+
if (fs.existsSync(envFilePath)) {
|
|
767
|
+
fs.writeFileSync(
|
|
768
|
+
envFilePath,
|
|
769
|
+
fs
|
|
770
|
+
.readFileSync(envFilePath, 'utf8')
|
|
771
|
+
.replaceAll(`DEPLOY_ID=${deployId}`, `DEPLOY_ID=${replicaDeployId}`),
|
|
772
|
+
'utf8',
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
{
|
|
779
|
+
let replicaIndex = -1;
|
|
780
|
+
for (const replica of serverConf[host][replicaPath].replicas) {
|
|
781
|
+
replicaIndex++;
|
|
782
|
+
const replicaDeployId = `${deployId}-${serverConf[host][replicaPath].replicas[replicaIndex].slice(1)}`;
|
|
783
|
+
let replicaServerConf = JSON.parse(
|
|
784
|
+
fs.readFileSync(`./engine-private/replica/${replicaDeployId}/conf.server.json`, 'utf8'),
|
|
785
|
+
);
|
|
786
|
+
|
|
787
|
+
const singleReplicaConf = replicaServerConf[host][replicaPath];
|
|
788
|
+
singleReplicaConf.replicas = undefined;
|
|
789
|
+
singleReplicaConf.singleReplica = undefined;
|
|
790
|
+
|
|
791
|
+
replicaServerConf = {};
|
|
792
|
+
replicaServerConf[host] = {};
|
|
793
|
+
replicaServerConf[host][replica] = singleReplicaConf;
|
|
794
|
+
|
|
795
|
+
fs.writeFileSync(
|
|
796
|
+
`./engine-private/replica/${replicaDeployId}/conf.server.json`,
|
|
797
|
+
JSON.stringify(replicaServerConf, null, 4),
|
|
798
|
+
'utf8',
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
if (!options.syncEnvPort) return resolve(true);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// Handle syncEnvPort operation
|
|
807
|
+
if (options.syncEnvPort) {
|
|
808
|
+
const dataDeploy = await getDataDeploy({ disableSyncEnvPort: true });
|
|
809
|
+
const dataEnv = [
|
|
810
|
+
{ env: 'production', port: 3000 },
|
|
811
|
+
{ env: 'development', port: 4000 },
|
|
812
|
+
{ env: 'test', port: 5000 },
|
|
813
|
+
];
|
|
814
|
+
let portOffset = 0;
|
|
815
|
+
const singleReplicaPortOffsets = {};
|
|
816
|
+
for (const deployIdObj of dataDeploy) {
|
|
817
|
+
const { deployId } = deployIdObj;
|
|
818
|
+
const baseConfPath = fs.existsSync(`./engine-private/replica/${deployId}`)
|
|
819
|
+
? `./engine-private/replica`
|
|
820
|
+
: `./engine-private/conf`;
|
|
821
|
+
|
|
822
|
+
const effectivePortOffset =
|
|
823
|
+
singleReplicaPortOffsets[deployId] !== undefined ? singleReplicaPortOffsets[deployId] : portOffset;
|
|
824
|
+
|
|
825
|
+
let skipDeploy = false;
|
|
826
|
+
for (const envInstanceObj of dataEnv) {
|
|
827
|
+
const envPath = `${baseConfPath}/${deployId}/.env.${envInstanceObj.env}`;
|
|
828
|
+
if (!fs.existsSync(envPath)) {
|
|
829
|
+
logger.warn(`Skipping ${deployId}: ${envPath} not found`);
|
|
830
|
+
skipDeploy = true;
|
|
831
|
+
break;
|
|
832
|
+
}
|
|
833
|
+
const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
834
|
+
envObj.PORT = `${envInstanceObj.port + effectivePortOffset}`;
|
|
835
|
+
writeEnv(envPath, envObj);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (skipDeploy) continue;
|
|
839
|
+
if (singleReplicaPortOffsets[deployId] !== undefined) continue;
|
|
840
|
+
|
|
841
|
+
const serverConf = loadReplicas(
|
|
842
|
+
deployId,
|
|
843
|
+
loadConfServerJson(`${baseConfPath}/${deployId}/conf.server.json`),
|
|
844
|
+
);
|
|
845
|
+
for (const host of Object.keys(serverConf)) {
|
|
846
|
+
let deferredSingleReplicaSlots = [];
|
|
847
|
+
for (const path of Object.keys(serverConf[host])) {
|
|
848
|
+
if (serverConf[host][path].singleReplica && serverConf[host][path].replicas) {
|
|
849
|
+
deferredSingleReplicaSlots.push({
|
|
850
|
+
replicas: serverConf[host][path].replicas,
|
|
851
|
+
peer: !!serverConf[host][path].peer,
|
|
852
|
+
});
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
portOffset++;
|
|
856
|
+
if (serverConf[host][path].peer) portOffset++;
|
|
857
|
+
}
|
|
858
|
+
for (const slot of deferredSingleReplicaSlots) {
|
|
859
|
+
for (const replica of slot.replicas) {
|
|
860
|
+
const replicaDeployId = buildReplicaId({ deployId, replica });
|
|
861
|
+
singleReplicaPortOffsets[replicaDeployId] = portOffset;
|
|
862
|
+
portOffset++;
|
|
863
|
+
if (slot.peer) portOffset++;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return resolve(true);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Handle buildFullClient operation (default)
|
|
872
|
+
{
|
|
873
|
+
const { deployId: resolvedDeployId } = loadConf(deployId, subConf ?? '');
|
|
874
|
+
|
|
875
|
+
let argHost = host ? host.split(',') : [];
|
|
876
|
+
let argPath = path ? path.split(',') : [];
|
|
877
|
+
let deployIdSingleReplicas = [];
|
|
878
|
+
let singleReplicaHosts = [];
|
|
879
|
+
const isReplicaContext = resolvedDeployId
|
|
880
|
+
? fs.existsSync(`./engine-private/replica/${resolvedDeployId}`)
|
|
881
|
+
: false;
|
|
882
|
+
const serverConf = resolvedDeployId
|
|
883
|
+
? readConfJson(resolvedDeployId, 'server', { loadReplicas: true })
|
|
884
|
+
: Config.default.server;
|
|
885
|
+
const confFilePath = resolvedDeployId ? getConfFilePath(resolvedDeployId, 'server') : null;
|
|
886
|
+
const originalConfBackup = confFilePath ? fs.readFileSync(confFilePath, 'utf8') : null;
|
|
887
|
+
for (const host of Object.keys(serverConf)) {
|
|
888
|
+
for (const path of Object.keys(serverConf[host])) {
|
|
889
|
+
if (argHost.length && argPath.length && (!argHost.includes(host) || !argPath.includes(path))) {
|
|
890
|
+
delete serverConf[host][path];
|
|
891
|
+
} else {
|
|
892
|
+
if (!isReplicaContext && serverConf[host][path].singleReplica && serverConf[host][path].replicas) {
|
|
893
|
+
singleReplicaHosts.push({ host, path });
|
|
894
|
+
deployIdSingleReplicas = deployIdSingleReplicas.concat(
|
|
895
|
+
serverConf[host][path].replicas.map((replica) =>
|
|
896
|
+
buildReplicaId({ deployId: resolvedDeployId, replica }),
|
|
897
|
+
),
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
await buildClient({
|
|
904
|
+
deployId: resolvedDeployId,
|
|
905
|
+
buildZip: options.buildZip || false,
|
|
906
|
+
split: options.split || '',
|
|
907
|
+
fullBuild: options.liteBuild ? false : true,
|
|
908
|
+
iconsBuild: options.iconsBuild || false,
|
|
909
|
+
});
|
|
910
|
+
for (const replicaDeployId of deployIdSingleReplicas) {
|
|
911
|
+
if (!fs.existsSync(`./engine-private/replica/${replicaDeployId}`)) {
|
|
912
|
+
logger.warn('Skip replica client build: replica folder not found', { replicaDeployId });
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
await Underpost.repo.client(replicaDeployId, '', '', '', {
|
|
916
|
+
buildZip: options.buildZip || false,
|
|
917
|
+
split: options.split || '',
|
|
918
|
+
liteBuild: options.liteBuild || false,
|
|
919
|
+
iconsBuild: options.iconsBuild || false,
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
return resolve(true);
|
|
924
|
+
}
|
|
925
|
+
} catch (error) {
|
|
926
|
+
console.log(error);
|
|
927
|
+
logger.error(error, error.stack);
|
|
928
|
+
return reject(false);
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
},
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* Gets a list of deleted files from a Git repository.
|
|
935
|
+
* @param {string} [path='.'] - The path to the repository.
|
|
936
|
+
* @returns {string[]} An array of deleted file paths.
|
|
937
|
+
* @memberof UnderpostRepository
|
|
938
|
+
*/
|
|
939
|
+
getDeleteFiles(path = '.') {
|
|
940
|
+
const commandUntrack = `cd ${path} && git ls-files --deleted`;
|
|
941
|
+
const diffUntrackOutput = shellExec(commandUntrack, { stdout: true, silent: true });
|
|
942
|
+
return diffUntrackOutput.toString().split('\n').filter(Boolean);
|
|
943
|
+
},
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Gets a list of changed (modified and untracked) files in a Git repository.
|
|
947
|
+
* @param {string} [path='.'] - The path to the repository.
|
|
948
|
+
* @param {string} [extension=''] - An optional file extension to filter by.
|
|
949
|
+
* @param {boolean} [head=false] - If true, diffs against HEAD^.
|
|
950
|
+
* @returns {string[]} An array of changed file paths.
|
|
951
|
+
* @memberof UnderpostRepository
|
|
952
|
+
*/
|
|
953
|
+
getChangedFiles(path = '.', extension = '', head = false) {
|
|
954
|
+
const extensionFilter = extension ? `-- '***.${extension}'` : '';
|
|
955
|
+
const command = `cd ${path} && git diff ${head ? 'HEAD^ HEAD ' : ''}--name-only ${extensionFilter}`;
|
|
956
|
+
const commandUntrack = `cd ${path} && git ls-files --others --exclude-standard`;
|
|
957
|
+
const diffOutput = shellExec(command, { stdout: true, silent: true });
|
|
958
|
+
const diffUntrackOutput = shellExec(commandUntrack, { stdout: true, silent: true });
|
|
959
|
+
const deleteFiles = Underpost.repo.getDeleteFiles(path);
|
|
960
|
+
return diffOutput
|
|
961
|
+
.toString()
|
|
962
|
+
.split('\n')
|
|
963
|
+
.filter(Boolean)
|
|
964
|
+
.concat(diffUntrackOutput.toString().split('\n').filter(Boolean))
|
|
965
|
+
.filter((f) => !deleteFiles.includes(f));
|
|
966
|
+
},
|
|
967
|
+
/**
|
|
968
|
+
* Updates the private configuration repository for a given deployId.
|
|
969
|
+
* @param {string} deployId - The deployment ID.
|
|
970
|
+
* @returns {{validVersion: boolean, engineVersion: string, deployVersion: string}} An object indicating if the versions are valid.
|
|
971
|
+
* @memberof UnderpostRepository
|
|
972
|
+
*/
|
|
973
|
+
privateConfUpdate(deployId) {
|
|
974
|
+
shellCd(`/home/dd/engine`);
|
|
975
|
+
const privateRepoName = `engine-${deployId.split('dd-')[1]}-private`;
|
|
976
|
+
const privateRepoPath = `../${privateRepoName}`;
|
|
977
|
+
if (fs.existsSync(privateRepoPath)) fs.removeSync(privateRepoPath);
|
|
978
|
+
shellExec(`cd .. && underpost clone ${process.env.GITHUB_USERNAME}/${privateRepoName}`);
|
|
979
|
+
shellExec(`cd ${privateRepoPath} && underpost pull . ${process.env.GITHUB_USERNAME}/${privateRepoName}`, {
|
|
980
|
+
silent: true,
|
|
981
|
+
});
|
|
982
|
+
const packageJsonDeploy = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/package.json`, 'utf8'));
|
|
983
|
+
const packageJsonEngine = JSON.parse(fs.readFileSync(`./package.json`, 'utf8'));
|
|
984
|
+
if (packageJsonDeploy.version !== packageJsonEngine.version) {
|
|
985
|
+
logger.warn(
|
|
986
|
+
`Version mismatch: deploy-version:${packageJsonDeploy.version} !== engine-version:${packageJsonEngine.version},
|
|
987
|
+
Prevent build private config repo.`,
|
|
988
|
+
);
|
|
989
|
+
return {
|
|
990
|
+
validVersion: false,
|
|
991
|
+
engineVersion: packageJsonEngine.version,
|
|
992
|
+
deployVersion: packageJsonDeploy.version,
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
shellExec(`node bin/build ${deployId} --conf`);
|
|
996
|
+
return {
|
|
997
|
+
validVersion: true,
|
|
998
|
+
engineVersion: packageJsonEngine.version,
|
|
999
|
+
deployVersion: packageJsonDeploy.version,
|
|
1000
|
+
};
|
|
1001
|
+
},
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* Retrieves the Git commit history.
|
|
1005
|
+
* @param {number} [sinceCommit=1] - The number of recent commits to retrieve.
|
|
1006
|
+
* @param {string} [repoPath='.'] - The path to the repository.
|
|
1007
|
+
* @returns {Array<{hash: string, message: string, files: string}>} An array of commit objects with hash, message, and files.
|
|
1008
|
+
* @memberof UnderpostRepository
|
|
1009
|
+
*/
|
|
1010
|
+
getHistory(sinceCommit = 1, repoPath = '.') {
|
|
1011
|
+
return shellExec(`cd ${repoPath} && git log -1 --pretty=format:"%h %s" -n ${sinceCommit}`, {
|
|
1012
|
+
stdout: true,
|
|
1013
|
+
silent: true,
|
|
1014
|
+
disableLog: true,
|
|
1015
|
+
})
|
|
1016
|
+
.split(`\n`)
|
|
1017
|
+
.map((line) => {
|
|
1018
|
+
const hash = line.split(' ')[0];
|
|
1019
|
+
return {
|
|
1020
|
+
hash,
|
|
1021
|
+
message: line.split(`${hash} `)[1],
|
|
1022
|
+
};
|
|
1023
|
+
})
|
|
1024
|
+
.filter((line) => line.hash)
|
|
1025
|
+
.map((line) => {
|
|
1026
|
+
line.files = shellExec(`cd ${repoPath} && git show --name-status --pretty="" ${line.hash}`, {
|
|
1027
|
+
stdout: true,
|
|
1028
|
+
silent: true,
|
|
1029
|
+
disableLog: true,
|
|
1030
|
+
});
|
|
1031
|
+
return line;
|
|
1032
|
+
});
|
|
1033
|
+
},
|
|
1034
|
+
/**
|
|
1035
|
+
* Updates the default configuration file based on the provided options.
|
|
1036
|
+
* @param {object} [options={ deployId: '' }] - The options for updating the configuration.
|
|
1037
|
+
* @param {string} [options.deployId=''] - The deployment ID to use for configuration.
|
|
1038
|
+
* @param {string} [options.confWorkflowId=''] - The configuration workflow ID to use.
|
|
1039
|
+
* @memberof UnderpostRepository
|
|
1040
|
+
*/
|
|
1041
|
+
updateDefaultConf(options = { deployId: '', confWorkflowId: '' }) {
|
|
1042
|
+
const defaultServer = DefaultConf.server['default.net']['/'];
|
|
1043
|
+
let { deployId, confWorkflowId } = options;
|
|
1044
|
+
let defaultConf = false;
|
|
1045
|
+
|
|
1046
|
+
// Custom workflow configurations
|
|
1047
|
+
if (confWorkflowId)
|
|
1048
|
+
switch (confWorkflowId) {
|
|
1049
|
+
case 'dd-github-pages': {
|
|
1050
|
+
const host = `${process.env.GITHUB_USERNAME ? process.env.GITHUB_USERNAME : 'underpostnet'}.github.io`;
|
|
1051
|
+
const path = '/pwa-microservices-template-ghpkg';
|
|
1052
|
+
DefaultConf.server = {
|
|
1053
|
+
[host]: { [path]: defaultServer },
|
|
1054
|
+
};
|
|
1055
|
+
DefaultConf.server[host][path].apiBaseProxyPath = '/';
|
|
1056
|
+
DefaultConf.server[host][path].apiBaseHost = 'www.nexodev.org';
|
|
1057
|
+
defaultConf = true;
|
|
1058
|
+
break;
|
|
1059
|
+
}
|
|
1060
|
+
case 'template': {
|
|
1061
|
+
const host = 'default.net';
|
|
1062
|
+
const path = '/';
|
|
1063
|
+
DefaultConf.server[host][path].valkey = {
|
|
1064
|
+
port: 'env:VALKEY_PORT:int:6379',
|
|
1065
|
+
host: 'env:VALKEY_HOST:127.0.0.1',
|
|
1066
|
+
};
|
|
1067
|
+
DefaultConf.server[host][path].db.host = 'env:DB_HOST:mongodb://127.0.0.1:27017';
|
|
1068
|
+
DefaultConf.server[host][path].db.replicaSet = 'env:DB_REPLICA_SET:rs0';
|
|
1069
|
+
DefaultConf.server[host][path].db.authSource = 'env:DB_AUTH_SOURCE:admin';
|
|
1070
|
+
DefaultConf.server[host][path].db.user = 'env:DB_USER:';
|
|
1071
|
+
DefaultConf.server[host][path].db.password = 'env:DB_PASSWORD:';
|
|
1072
|
+
defaultConf = true;
|
|
1073
|
+
break;
|
|
1074
|
+
}
|
|
1075
|
+
default:
|
|
1076
|
+
logger.error(`Unknown confWorkflowId: ${confWorkflowId}.`);
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
else if (deployId && fs.existsSync(`./engine-private/conf/${deployId}`)) {
|
|
1080
|
+
DefaultConf.client = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.client.json`, 'utf8'));
|
|
1081
|
+
DefaultConf.server = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
|
|
1082
|
+
DefaultConf.ssr = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.ssr.json`, 'utf8'));
|
|
1083
|
+
} else
|
|
1084
|
+
logger.warn(
|
|
1085
|
+
`Deploy ID configuration not found: ./engine-private/conf/${deployId}, using default configuration.`,
|
|
1086
|
+
);
|
|
1087
|
+
|
|
1088
|
+
// Serialize the configuration into the conf.*.js manifest file.
|
|
1089
|
+
// env: references from JSON configs are preserved as 'env:KEY' strings.
|
|
1090
|
+
// At runtime, resolveConfSecrets() in conf.js resolves them via process.env.
|
|
1091
|
+
const sepRender = '/**/';
|
|
1092
|
+
const confRawPaths = fs.readFileSync('./conf.js', 'utf8').split(sepRender);
|
|
1093
|
+
confRawPaths[1] = `${JSON.stringify(DefaultConf)};`;
|
|
1094
|
+
const targetConfPath = `./conf${defaultConf ? '' : `.${deployId}`}.js`;
|
|
1095
|
+
fs.writeFileSync(targetConfPath, confRawPaths.join(sepRender), 'utf8');
|
|
1096
|
+
shellExec(`prettier --write ${targetConfPath}`);
|
|
1097
|
+
},
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Cleans the specified paths in the repository by resetting, checking out, and cleaning untracked files.
|
|
1101
|
+
* @param {object} [options={ paths: [''] }] - The options for cleaning.
|
|
1102
|
+
* @param {string[]} [options.paths=['']] - The paths to clean.
|
|
1103
|
+
* @memberof UnderpostRepository
|
|
1104
|
+
*/
|
|
1105
|
+
clean(options = { paths: [''] }) {
|
|
1106
|
+
for (const path of options.paths) {
|
|
1107
|
+
shellExec(`cd ${path} && git reset`, { silentOnError: true, silent: true, disableLog: true });
|
|
1108
|
+
shellExec(`cd ${path} && git checkout .`, { silentOnError: true, silent: true, disableLog: true });
|
|
1109
|
+
shellExec(`cd ${path} && git clean -f -d`, { silentOnError: true, silent: true, disableLog: true });
|
|
1110
|
+
}
|
|
1111
|
+
},
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* Copies files recursively from a Git repository URL directory path.
|
|
1115
|
+
* @param {object} options - Configuration options for copying files.
|
|
1116
|
+
* @param {string} options.gitUrl - The GitHub repository URL (e.g., 'https://github.com/canonical/packer-maas').
|
|
1117
|
+
* @param {string} options.directoryPath - The directory path within the repository to copy (e.g., 'rocky-9').
|
|
1118
|
+
* @param {string} options.targetPath - The local target path where files should be copied.
|
|
1119
|
+
* @param {string} [options.branch='main'] - The git branch to use (default: 'main').
|
|
1120
|
+
* @param {boolean} [options.overwrite=false] - Whether to overwrite existing target directory.
|
|
1121
|
+
* @returns {Promise<object>} A promise that resolves with copied files information.
|
|
1122
|
+
* @memberof UnderpostRepository
|
|
1123
|
+
*/
|
|
1124
|
+
async copyGitUrlDirectoryRecursive(options) {
|
|
1125
|
+
const { gitUrl, directoryPath, targetPath, branch = 'main', overwrite = false } = options;
|
|
1126
|
+
|
|
1127
|
+
// Validate inputs
|
|
1128
|
+
if (!gitUrl) {
|
|
1129
|
+
throw new Error('gitUrl is required');
|
|
1130
|
+
}
|
|
1131
|
+
if (!directoryPath) {
|
|
1132
|
+
throw new Error('directoryPath is required');
|
|
1133
|
+
}
|
|
1134
|
+
if (!targetPath) {
|
|
1135
|
+
throw new Error('targetPath is required');
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// Parse GitHub URL to extract owner and repo
|
|
1139
|
+
const urlMatch = gitUrl.match(/github\.com\/([^\/]+)\/([^\/\.]+)/);
|
|
1140
|
+
if (!urlMatch) {
|
|
1141
|
+
throw new Error(`Invalid GitHub URL: ${gitUrl}`);
|
|
1142
|
+
}
|
|
1143
|
+
const [, owner, repo] = urlMatch;
|
|
1144
|
+
|
|
1145
|
+
logger.info(`Copying from ${owner}/${repo}/${directoryPath} to ${targetPath}`);
|
|
1146
|
+
|
|
1147
|
+
// Check if target directory exists
|
|
1148
|
+
if (fs.existsSync(targetPath) && !overwrite) {
|
|
1149
|
+
throw new Error(`Target directory already exists: ${targetPath}. Use overwrite option to replace.`);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// Create target directory
|
|
1153
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
1154
|
+
|
|
1155
|
+
// GitHub API base URL
|
|
1156
|
+
const githubApiBase = 'https://api.github.com/repos';
|
|
1157
|
+
const apiUrl = `${githubApiBase}/${owner}/${repo}/contents/${directoryPath}`;
|
|
1158
|
+
|
|
1159
|
+
logger.info(`Fetching directory contents from: ${apiUrl}`);
|
|
1160
|
+
|
|
1161
|
+
try {
|
|
1162
|
+
// Fetch directory contents recursively
|
|
1163
|
+
const copiedFiles = await this.fetchAndCopyGitHubDirectory({
|
|
1164
|
+
apiUrl,
|
|
1165
|
+
targetPath,
|
|
1166
|
+
basePath: directoryPath,
|
|
1167
|
+
branch,
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
logger.info(`Successfully copied ${copiedFiles.length} files to ${targetPath}`);
|
|
1171
|
+
|
|
1172
|
+
return {
|
|
1173
|
+
success: true,
|
|
1174
|
+
filesCount: copiedFiles.length,
|
|
1175
|
+
files: copiedFiles,
|
|
1176
|
+
targetPath,
|
|
1177
|
+
};
|
|
1178
|
+
} catch (error) {
|
|
1179
|
+
// Clean up on error
|
|
1180
|
+
if (fs.existsSync(targetPath)) {
|
|
1181
|
+
fs.removeSync(targetPath);
|
|
1182
|
+
logger.warn(`Cleaned up target directory after error: ${targetPath}`);
|
|
1183
|
+
}
|
|
1184
|
+
throw new Error(`Failed to copy directory: ${error.message}`);
|
|
1185
|
+
}
|
|
1186
|
+
},
|
|
1187
|
+
|
|
1188
|
+
/**
|
|
1189
|
+
* Internal method to recursively fetch and copy files from GitHub API.
|
|
1190
|
+
* @method
|
|
1191
|
+
* @param {object} options - Fetch options.
|
|
1192
|
+
* @param {string} options.apiUrl - The GitHub API URL.
|
|
1193
|
+
* @param {string} options.targetPath - The local target path.
|
|
1194
|
+
* @param {string} options.basePath - The base path in the repository.
|
|
1195
|
+
* @param {string} options.branch - The git branch.
|
|
1196
|
+
* @returns {Promise<array>} Array of copied file paths.
|
|
1197
|
+
* @memberof UnderpostRepository
|
|
1198
|
+
*/
|
|
1199
|
+
async fetchAndCopyGitHubDirectory(options) {
|
|
1200
|
+
const { apiUrl, targetPath, basePath, branch } = options;
|
|
1201
|
+
const copiedFiles = [];
|
|
1202
|
+
|
|
1203
|
+
const response = await fetch(apiUrl, {
|
|
1204
|
+
headers: {
|
|
1205
|
+
Accept: 'application/vnd.github.v3+json',
|
|
1206
|
+
'User-Agent': 'underpost-cli',
|
|
1207
|
+
},
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
if (!response.ok) {
|
|
1211
|
+
const errorBody = await response.text();
|
|
1212
|
+
logger.error(`GitHub API request failed for: ${apiUrl}`);
|
|
1213
|
+
logger.error(`Status: ${response.status} ${response.statusText}`);
|
|
1214
|
+
logger.error(`Response: ${errorBody}`);
|
|
1215
|
+
throw new Error(`GitHub API request failed: ${response.status} ${response.statusText} - ${errorBody}`);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
const contents = await response.json();
|
|
1219
|
+
|
|
1220
|
+
if (!Array.isArray(contents)) {
|
|
1221
|
+
logger.error(`Expected directory but got: ${typeof contents}`);
|
|
1222
|
+
logger.error(`API URL: ${apiUrl}`);
|
|
1223
|
+
logger.error(`Response keys: ${Object.keys(contents).join(', ')}`);
|
|
1224
|
+
if (contents.message) {
|
|
1225
|
+
logger.error(`GitHub message: ${contents.message}`);
|
|
1226
|
+
}
|
|
1227
|
+
throw new Error(
|
|
1228
|
+
`Path is not a directory: ${basePath}. Response: ${JSON.stringify(contents).substring(0, 200)}`,
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
logger.info(`Found ${contents.length} items in directory: ${basePath}`);
|
|
1233
|
+
|
|
1234
|
+
for (const item of contents) {
|
|
1235
|
+
const itemTargetPath = `${targetPath}/${item.name}`;
|
|
1236
|
+
|
|
1237
|
+
if (item.type === 'file') {
|
|
1238
|
+
logger.info(`Downloading file: ${item.path}`);
|
|
1239
|
+
|
|
1240
|
+
const fileResponse = await fetch(item.download_url);
|
|
1241
|
+
if (!fileResponse.ok) {
|
|
1242
|
+
logger.error(`Failed to download: ${item.download_url}`);
|
|
1243
|
+
throw new Error(`Failed to download file: ${item.path} (${fileResponse.status})`);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
const fileContent = await fileResponse.text();
|
|
1247
|
+
fs.writeFileSync(itemTargetPath, fileContent);
|
|
1248
|
+
|
|
1249
|
+
logger.info(`Saved: ${itemTargetPath}`);
|
|
1250
|
+
copiedFiles.push(itemTargetPath);
|
|
1251
|
+
} else if (item.type === 'dir') {
|
|
1252
|
+
logger.info(`Processing directory: ${item.path}`);
|
|
1253
|
+
|
|
1254
|
+
fs.mkdirSync(itemTargetPath, { recursive: true });
|
|
1255
|
+
|
|
1256
|
+
const subFiles = await this.fetchAndCopyGitHubDirectory({
|
|
1257
|
+
apiUrl: item.url,
|
|
1258
|
+
targetPath: itemTargetPath,
|
|
1259
|
+
basePath: item.path,
|
|
1260
|
+
branch,
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
copiedFiles.push(...subFiles);
|
|
1264
|
+
logger.info(`Completed directory: ${item.path} (${subFiles.length} files)`);
|
|
1265
|
+
} else {
|
|
1266
|
+
logger.warn(`Skipping unknown item type '${item.type}': ${item.path}`);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
return copiedFiles;
|
|
1271
|
+
},
|
|
1272
|
+
|
|
1273
|
+
/**
|
|
1274
|
+
* Resolves the default branch for a remote GitHub repository by querying
|
|
1275
|
+
* `git ls-remote --symref` and extracting the HEAD ref target.
|
|
1276
|
+
* Falls back to `main` when detection fails.
|
|
1277
|
+
* @param {string} repo - The GitHub repository (e.g., "owner/repo").
|
|
1278
|
+
* @returns {string} The default branch name (e.g. "main" or "master").
|
|
1279
|
+
* @memberof UnderpostRepository
|
|
1280
|
+
*/
|
|
1281
|
+
getDefaultBranch(repo) {
|
|
1282
|
+
if (!repo) return 'main';
|
|
1283
|
+
const authUrl = Underpost.repo.resolveAuthUrl(repo);
|
|
1284
|
+
const raw = shellExec(
|
|
1285
|
+
`GIT_TERMINAL_PROMPT=0 git -c credential.helper= ls-remote --symref "${authUrl}" HEAD 2>&1`,
|
|
1286
|
+
{ stdout: true, silent: true, disableLog: true, silentOnError: true },
|
|
1287
|
+
);
|
|
1288
|
+
// --symref emits a line like: ref: refs/heads/main HEAD
|
|
1289
|
+
const match = typeof raw === 'string' ? raw.match(/^ref:\s*refs\/heads\/(\S+)\tHEAD$/m) : null;
|
|
1290
|
+
const branch = match ? match[1] : 'main';
|
|
1291
|
+
logger.info('getDefaultBranch', { repo, branch });
|
|
1292
|
+
return branch;
|
|
1293
|
+
},
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* Dispatches a GitHub Actions workflow using gh CLI or curl fallback.
|
|
1297
|
+
* @param {object} options - Dispatch options.
|
|
1298
|
+
* @param {string} options.repo - The GitHub repository (e.g., "owner/repo").
|
|
1299
|
+
* @param {string} options.workflowFile - The workflow file name (e.g., "engine-core.cd.yml").
|
|
1300
|
+
* @param {string} [options.ref] - The git ref to dispatch against. Auto-detects the remote's default branch when omitted.
|
|
1301
|
+
* @param {object} [options.inputs={}] - Key-value inputs for the workflow_dispatch event.
|
|
1302
|
+
* @memberof UnderpostRepository
|
|
1303
|
+
*/
|
|
1304
|
+
dispatchWorkflow(options = { repo: '', workflowFile: '', ref: '', inputs: {} }) {
|
|
1305
|
+
const { repo, workflowFile, inputs } = options;
|
|
1306
|
+
const ref = options.ref || Underpost.repo.getDefaultBranch(repo);
|
|
1307
|
+
const ghAvailable = shellExec('command -v gh 2>/dev/null', {
|
|
1308
|
+
stdout: true,
|
|
1309
|
+
silent: true,
|
|
1310
|
+
disableLog: true,
|
|
1311
|
+
}).trim();
|
|
1312
|
+
|
|
1313
|
+
if (ghAvailable) {
|
|
1314
|
+
let cmd = `gh workflow run ${workflowFile} --repo ${repo} --ref ${ref}`;
|
|
1315
|
+
for (const [key, value] of Object.entries(inputs)) {
|
|
1316
|
+
if (value !== undefined && value !== '') {
|
|
1317
|
+
const escaped = String(value).replace(/'/g, "'\\''");
|
|
1318
|
+
cmd += ` -f ${key}='${escaped}'`;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
shellExec(cmd);
|
|
1322
|
+
} else {
|
|
1323
|
+
let token = process.env.GITHUB_TOKEN;
|
|
1324
|
+
if (!token) {
|
|
1325
|
+
const envPath = `${getNpmRootPath()}/underpost/.env`;
|
|
1326
|
+
if (fs.existsSync(envPath) && fs.statSync(envPath).isFile()) {
|
|
1327
|
+
const envVars = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
1328
|
+
token = envVars.GITHUB_TOKEN;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
if (!token) {
|
|
1332
|
+
logger.error('GITHUB_TOKEN is required for workflow dispatch (gh CLI not available)');
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
const payload = { ref };
|
|
1336
|
+
if (Object.keys(inputs).length > 0) payload.inputs = inputs;
|
|
1337
|
+
const payloadJson = JSON.stringify(payload).replace(/'/g, "'\\''");
|
|
1338
|
+
shellExec(
|
|
1339
|
+
`curl -s -f -X POST ` +
|
|
1340
|
+
`-H "Accept: application/vnd.github.v3+json" ` +
|
|
1341
|
+
`-H "Authorization: token ${token}" ` +
|
|
1342
|
+
`"https://api.github.com/repos/${repo}/actions/workflows/${workflowFile}/dispatches" ` +
|
|
1343
|
+
`-d '${payloadJson}'`,
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
logger.info('Dispatched workflow', `${repo} -> ${workflowFile}`, inputs.job ? `(job: ${inputs.job})` : '');
|
|
1347
|
+
},
|
|
1348
|
+
|
|
1349
|
+
/**
|
|
1350
|
+
* Resolves a Git remote URL, normalizing short-form owner/repo references to full
|
|
1351
|
+
* GitHub HTTPS URLs and injecting GITHUB_TOKEN when available.
|
|
1352
|
+
* @param {string} url - The repository URL or short-form (e.g. "owner/repo" or full HTTPS URL).
|
|
1353
|
+
* @returns {string} The resolved (and optionally authenticated) HTTPS URL.
|
|
1354
|
+
* @memberof UnderpostRepository
|
|
1355
|
+
*/
|
|
1356
|
+
resolveAuthUrl(url) {
|
|
1357
|
+
if (!url) return url;
|
|
1358
|
+
// Normalize short form "owner/repo" → full GitHub HTTPS URL
|
|
1359
|
+
let normalized = url;
|
|
1360
|
+
if (!url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('git@')) {
|
|
1361
|
+
normalized = `https://github.com/${url}`;
|
|
1362
|
+
}
|
|
1363
|
+
if (process.env.GITHUB_TOKEN && normalized.startsWith('https://github.com/')) {
|
|
1364
|
+
return normalized.replace(
|
|
1365
|
+
'https://github.com/',
|
|
1366
|
+
`https://x-access-token:${process.env.GITHUB_TOKEN}@github.com/`,
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
return normalized;
|
|
1370
|
+
},
|
|
1371
|
+
/**
|
|
1372
|
+
* Checks whether a remote Git repository URL is reachable.
|
|
1373
|
+
* Uses `silentOnError` so a non-reachable remote returns false instead of throwing.
|
|
1374
|
+
* Injects `GITHUB_TOKEN` into GitHub HTTPS URLs when available.
|
|
1375
|
+
* @param {string} url - Full HTTPS clone URL to test (e.g. "https://github.com/org/repo.git").
|
|
1376
|
+
* @returns {boolean} `true` when the remote responded with at least one ref hash.
|
|
1377
|
+
* @memberof UnderpostRepository
|
|
1378
|
+
*/
|
|
1379
|
+
isRemoteRepo(url) {
|
|
1380
|
+
if (!url) return false;
|
|
1381
|
+
const authUrl = Underpost.repo.resolveAuthUrl(url);
|
|
1382
|
+
// GIT_TERMINAL_PROMPT=0 prevents git from hanging on credential prompts inside containers.
|
|
1383
|
+
// `-c credential.helper=` disables any host-configured credential helper so its stderr
|
|
1384
|
+
// warnings don't pollute the ls-remote output; the token is already embedded in authUrl.
|
|
1385
|
+
const raw = shellExec(`GIT_TERMINAL_PROMPT=0 git -c credential.helper= ls-remote "${authUrl}" HEAD 2>&1`, {
|
|
1386
|
+
stdout: true,
|
|
1387
|
+
silent: true,
|
|
1388
|
+
disableLog: true,
|
|
1389
|
+
silentOnError: true,
|
|
1390
|
+
});
|
|
1391
|
+
const refLine = typeof raw === 'string' ? (raw.match(/^[0-9a-f]{40}\t.*$/m) || [])[0] : undefined;
|
|
1392
|
+
const accessible = !!refLine;
|
|
1393
|
+
logger.info('isRemoteRepo', { url, accessible, ref: refLine || (raw || '').trim().split('\n').pop() });
|
|
1394
|
+
return accessible;
|
|
1395
|
+
},
|
|
1396
|
+
|
|
1397
|
+
/**
|
|
1398
|
+
* Returns the current URL of a git remote in plain text.
|
|
1399
|
+
* @param {object} [opts]
|
|
1400
|
+
* @param {string} [opts.path='.'] - Path to the git repository.
|
|
1401
|
+
* @param {string} [opts.remote='origin'] - Remote name to query.
|
|
1402
|
+
* @returns {string} The remote URL, or '' when the remote is not configured.
|
|
1403
|
+
* @memberof UnderpostRepository
|
|
1404
|
+
*/
|
|
1405
|
+
getRemoteUrl({ path: repoPath = '.', remote = 'origin' } = {}) {
|
|
1406
|
+
return shellExec(`cd "${repoPath}" && git remote get-url ${remote}`, {
|
|
1407
|
+
stdout: true,
|
|
1408
|
+
silent: true,
|
|
1409
|
+
disableLog: true,
|
|
1410
|
+
silentOnError: true,
|
|
1411
|
+
}).trim();
|
|
1412
|
+
},
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* Switches a local repository onto a different remote and force-syncs its
|
|
1416
|
+
* working tree to a target branch, discarding local commits and tracked
|
|
1417
|
+
* changes — effectively "switch repo to <url>#<branch>".
|
|
1418
|
+
*
|
|
1419
|
+
* Sequence (idempotent, re-runnable):
|
|
1420
|
+
* 1. Normalize the URL (`owner/repo` → full GitHub HTTPS) and set/add the
|
|
1421
|
+
* remote, storing the token-free URL so no secret leaks into `.git/config`.
|
|
1422
|
+
* 2. Force-fetch the target branch (auth injected inline for private repos).
|
|
1423
|
+
* 3. Reset the working tree to the fetched tip and check out the target
|
|
1424
|
+
* branch, overwriting any current tracked content.
|
|
1425
|
+
*
|
|
1426
|
+
* Untracked files are intentionally left in place (no `git clean`).
|
|
1427
|
+
*
|
|
1428
|
+
* @param {object} opts
|
|
1429
|
+
* @param {string} opts.url - New remote URL (full URL or "owner/repo" short form).
|
|
1430
|
+
* @param {string} [opts.path='.'] - Path to the git repository.
|
|
1431
|
+
* @param {string} [opts.branch='master'] - Target branch to overwrite the current tree with.
|
|
1432
|
+
* @param {string} [opts.remote='origin'] - Remote name to set and fetch from.
|
|
1433
|
+
* @returns {void}
|
|
1434
|
+
* @memberof UnderpostRepository
|
|
1435
|
+
*/
|
|
1436
|
+
switchRemote({ url, path: repoPath = '.', branch = 'master', remote = 'origin' }) {
|
|
1437
|
+
if (!url) throw new Error('switchRemote requires a target remote url');
|
|
1438
|
+
if (!fs.existsSync(`${repoPath}/.git`)) throw new Error(`switchRemote: not a git repository: ${repoPath}`);
|
|
1439
|
+
// Token-free URL for the stored remote; auth-injected URL only for the fetch.
|
|
1440
|
+
let normalized = url;
|
|
1441
|
+
if (!url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('git@')) {
|
|
1442
|
+
normalized = `https://github.com/${url}`;
|
|
1443
|
+
}
|
|
1444
|
+
const authUrl = Underpost.repo.resolveAuthUrl(url);
|
|
1445
|
+
const current = Underpost.repo.getRemoteUrl({ path: repoPath, remote });
|
|
1446
|
+
if (!current) shellExec(`cd "${repoPath}" && git remote add ${remote} "${normalized}"`);
|
|
1447
|
+
else shellExec(`cd "${repoPath}" && git remote set-url ${remote} "${normalized}"`);
|
|
1448
|
+
logger.info('switchRemote', { path: repoPath, remote, branch, url: normalized });
|
|
1449
|
+
shellExec(`cd "${repoPath}" && GIT_TERMINAL_PROMPT=0 git fetch --force "${authUrl}" ${branch}`);
|
|
1450
|
+
// reset --hard first clears the worktree so the checkout cannot be blocked
|
|
1451
|
+
// by conflicting local changes; -B points the target branch at the fetched tip.
|
|
1452
|
+
shellExec(`cd "${repoPath}" && git reset --hard FETCH_HEAD`);
|
|
1453
|
+
shellExec(`cd "${repoPath}" && git checkout -B ${branch} FETCH_HEAD`);
|
|
1454
|
+
},
|
|
1455
|
+
|
|
1456
|
+
/**
|
|
1457
|
+
* Returns metadata about unpushed commits in a git repository.
|
|
1458
|
+
* Fetches from origin, then counts commits ahead of the remote branch.
|
|
1459
|
+
* @param {string} [repoPath='.'] - Path to the git repository.
|
|
1460
|
+
* @param {number} [fallback=1] - Value to return as `count` when no unpushed commits are detected.
|
|
1461
|
+
* @returns {{ count: number, branch: string, hasUnpushed: boolean }} Unpush metadata.
|
|
1462
|
+
* @memberof UnderpostRepository
|
|
1463
|
+
*/
|
|
1464
|
+
getUnpushedCount(repoPath = '.', fallback = 1) {
|
|
1465
|
+
// Every git call is silentOnError: a detached HEAD (CI checkout) or a missing upstream must
|
|
1466
|
+
// degrade to the fallback, never throw — otherwise the thrown error is logged to stdout and
|
|
1467
|
+
// can be captured as a commit message by callers that read this command's output.
|
|
1468
|
+
const branch = shellExec(`cd ${repoPath} && git branch --show-current`, {
|
|
1469
|
+
stdout: true,
|
|
1470
|
+
silent: true,
|
|
1471
|
+
disableLog: true,
|
|
1472
|
+
silentOnError: true,
|
|
1473
|
+
})
|
|
1474
|
+
.toString()
|
|
1475
|
+
.trim();
|
|
1476
|
+
if (!branch) return { count: fallback, branch: '', hasUnpushed: false };
|
|
1477
|
+
shellExec(`cd ${repoPath} && git fetch origin 2>/dev/null`, {
|
|
1478
|
+
silent: true,
|
|
1479
|
+
disableLog: true,
|
|
1480
|
+
silentOnError: true,
|
|
1481
|
+
});
|
|
1482
|
+
const raw = shellExec(`cd ${repoPath} && git rev-list --count origin/${branch}..HEAD 2>/dev/null`, {
|
|
1483
|
+
stdout: true,
|
|
1484
|
+
silent: true,
|
|
1485
|
+
disableLog: true,
|
|
1486
|
+
silentOnError: true,
|
|
1487
|
+
})
|
|
1488
|
+
.toString()
|
|
1489
|
+
.trim();
|
|
1490
|
+
const count = parseInt(raw);
|
|
1491
|
+
const hasUnpushed = !isNaN(count) && count > 0;
|
|
1492
|
+
return { count: hasUnpushed ? count : fallback, branch, hasUnpushed };
|
|
1493
|
+
},
|
|
1494
|
+
|
|
1495
|
+
/**
|
|
1496
|
+
* Sanitizes a markdown changelog string into a compact message format.
|
|
1497
|
+
* Strips date headers, converts section tags to `[tag]` prefixes, removes bullet markers and special characters.
|
|
1498
|
+
* @param {string} message - The raw markdown changelog output.
|
|
1499
|
+
* @returns {string} The sanitized single-line or multi-line compact message.
|
|
1500
|
+
* @memberof UnderpostRepository
|
|
1501
|
+
*/
|
|
1502
|
+
sanitizeChangelogMessage(message) {
|
|
1503
|
+
if (!message) return '';
|
|
1504
|
+
const sanitized = message
|
|
1505
|
+
.replace(/^##\s+\d{4}-\d{2}-\d{2}\s*/gm, '')
|
|
1506
|
+
.replace(/^###\s+(\S+)\s*/gm, '[$1] ')
|
|
1507
|
+
.replace(/^- /gm, '')
|
|
1508
|
+
.replaceAll('"', '')
|
|
1509
|
+
.replaceAll('`', '')
|
|
1510
|
+
.split('\n')
|
|
1511
|
+
.map((l) => l.trim())
|
|
1512
|
+
.filter(Boolean)
|
|
1513
|
+
.join('\n')
|
|
1514
|
+
.trim()
|
|
1515
|
+
.replaceAll('] - ', '] ');
|
|
1516
|
+
// The empty-changelog placeholder must never become a commit message; return empty so
|
|
1517
|
+
// callers fall back to their own generic default.
|
|
1518
|
+
return sanitized === 'No changelog entries found.' ? '' : sanitized;
|
|
1519
|
+
},
|
|
1520
|
+
/**
|
|
1521
|
+
* Initializes a git repository at the given path and configures user identity
|
|
1522
|
+
* from environment variables (`GITHUB_USERNAME` / `GITHUB_EMAIL`).
|
|
1523
|
+
* Safe to call on an already-initialized repo — only runs `git init` when
|
|
1524
|
+
* `.git` is absent and always ensures user.name / user.email are set.
|
|
1525
|
+
* @param {object} opts
|
|
1526
|
+
* @param {string} opts.path - Absolute or relative path to the repository.
|
|
1527
|
+
* @param {string} [opts.origin] - If provided, sets or updates git remote `origin`.
|
|
1528
|
+
* @memberof UnderpostRepository
|
|
1529
|
+
*/
|
|
1530
|
+
initLocalRepo({ path: repoPath, origin }) {
|
|
1531
|
+
const gitUsername = process.env.GITHUB_USERNAME || 'underpostnet';
|
|
1532
|
+
const gitEmail = process.env.GITHUB_EMAIL || `development@underpost.net`;
|
|
1533
|
+
|
|
1534
|
+
if (!fs.existsSync(`${repoPath}/.git`)) {
|
|
1535
|
+
shellExec(`mkdir -p "${repoPath}" && git init "${repoPath}"`);
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
shellExec(`cd "${repoPath}" && git config user.name '${gitUsername}'`);
|
|
1539
|
+
shellExec(`cd "${repoPath}" && git config user.email '${gitEmail}'`);
|
|
1540
|
+
shellExec(`cd "${repoPath}" && git config core.filemode false`);
|
|
1541
|
+
if (origin) {
|
|
1542
|
+
const currentRemote = shellExec(`cd "${repoPath}" && git remote get-url origin`, {
|
|
1543
|
+
stdout: true,
|
|
1544
|
+
silent: true,
|
|
1545
|
+
silentOnError: true,
|
|
1546
|
+
}).trim();
|
|
1547
|
+
if (!currentRemote) {
|
|
1548
|
+
shellExec(`cd "${repoPath}" && git remote add origin "${origin}"`);
|
|
1549
|
+
} else if (currentRemote !== origin) {
|
|
1550
|
+
shellExec(`cd "${repoPath}" && git remote set-url origin "${origin}"`);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
},
|
|
1554
|
+
/**
|
|
1555
|
+
* Manages a cron-backup Git repository: clone, pull, commit, or push.
|
|
1556
|
+
* Resolves the repository path as `../<repoName>` relative to the CWD.
|
|
1557
|
+
* Requires the `GITHUB_USERNAME` environment variable to be set.
|
|
1558
|
+
* @param {object} params
|
|
1559
|
+
* @param {string} params.repoName - Repository name (e.g. `engine-cyberia-cron-backups`).
|
|
1560
|
+
* @param {'clone'|'pull'|'commit'|'push'} params.operation - Git operation to perform.
|
|
1561
|
+
* @param {string} [params.message=''] - Commit message (used by the `commit` operation).
|
|
1562
|
+
* @param {boolean} [params.forceClone=false] - Remove existing clone before re-cloning.
|
|
1563
|
+
* @returns {boolean} `true` on success, `false` if GITHUB_USERNAME is unset or on error.
|
|
1564
|
+
* @memberof UnderpostRepository
|
|
1565
|
+
*/
|
|
1566
|
+
manageBackupRepo({ repoName, operation, message = '', forceClone = false }) {
|
|
1567
|
+
try {
|
|
1568
|
+
const username = process.env.GITHUB_USERNAME;
|
|
1569
|
+
if (!username) {
|
|
1570
|
+
logger.error('GITHUB_USERNAME environment variable not set');
|
|
1571
|
+
return false;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
const repoPath = `../${repoName}`;
|
|
1575
|
+
|
|
1576
|
+
switch (operation) {
|
|
1577
|
+
case 'clone':
|
|
1578
|
+
if (forceClone && fs.existsSync(repoPath)) {
|
|
1579
|
+
logger.info(`Force clone: removing existing repository: ${repoName}`);
|
|
1580
|
+
fs.removeSync(repoPath);
|
|
1581
|
+
}
|
|
1582
|
+
if (!fs.existsSync(repoPath)) {
|
|
1583
|
+
shellExec(`cd .. && underpost clone ${username}/${repoName}`);
|
|
1584
|
+
logger.info(`Cloned repository: ${repoName}`);
|
|
1585
|
+
}
|
|
1586
|
+
break;
|
|
1587
|
+
|
|
1588
|
+
case 'pull':
|
|
1589
|
+
if (fs.existsSync(repoPath)) {
|
|
1590
|
+
shellExec(`cd ${repoPath} && git checkout . && git clean -f -d`);
|
|
1591
|
+
shellExec(`cd ${repoPath} && underpost pull . ${username}/${repoName}`, { silent: true });
|
|
1592
|
+
logger.info(`Pulled repository: ${repoName}`);
|
|
1593
|
+
}
|
|
1594
|
+
break;
|
|
1595
|
+
|
|
1596
|
+
case 'commit':
|
|
1597
|
+
if (fs.existsSync(repoPath)) {
|
|
1598
|
+
shellExec(`cd ${repoPath} && git add .`);
|
|
1599
|
+
shellExec(`underpost cmt ${repoPath} backup '' '${message}'`);
|
|
1600
|
+
logger.info(`Committed to repository: ${repoName}`, { message });
|
|
1601
|
+
}
|
|
1602
|
+
break;
|
|
1603
|
+
|
|
1604
|
+
case 'push':
|
|
1605
|
+
if (fs.existsSync(repoPath)) {
|
|
1606
|
+
shellExec(`cd ${repoPath} && underpost push . ${username}/${repoName}`, { silent: true });
|
|
1607
|
+
logger.info(`Pushed repository: ${repoName}`);
|
|
1608
|
+
}
|
|
1609
|
+
break;
|
|
1610
|
+
|
|
1611
|
+
default:
|
|
1612
|
+
logger.warn(`Unknown git operation: ${operation}`);
|
|
1613
|
+
return false;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
return true;
|
|
1617
|
+
} catch (error) {
|
|
1618
|
+
logger.error(`Git operation failed`, { repoName, operation, error: error.message });
|
|
1619
|
+
return false;
|
|
1620
|
+
}
|
|
1621
|
+
},
|
|
1622
|
+
|
|
1623
|
+
/**
|
|
1624
|
+
* Resolves the in-pod site-root directory where a conf route's repository lives.
|
|
1625
|
+
*
|
|
1626
|
+
* General-purpose resolution, independent of any single runtime:
|
|
1627
|
+
* 1. An explicit `directory` (the conf-declared document root) always wins.
|
|
1628
|
+
* 2. Otherwise the runtime's base directory is used (e.g. `wp` → `/opt/lampp/htdocs/wp/<host>`).
|
|
1629
|
+
* 3. A subdirectory route (e.g. `/wp`) appends `<subDir>` to the resolved root.
|
|
1630
|
+
*
|
|
1631
|
+
* This mirrors {@link WpService.createApp}'s `vhostDir`/`wpDir` layout so backups target the
|
|
1632
|
+
* exact directory provisioning created, including subdirectory installs and custom directories.
|
|
1633
|
+
*
|
|
1634
|
+
* @param {object} opts
|
|
1635
|
+
* @param {string} opts.runtime - The runtime identifier (e.g. 'wp').
|
|
1636
|
+
* @param {string} opts.host - The virtual-host name.
|
|
1637
|
+
* @param {string} [opts.routePath='/'] - The conf route path the repository is mounted under.
|
|
1638
|
+
* @param {string} [opts.directory] - Explicit document root from conf; overrides the runtime base.
|
|
1639
|
+
* @returns {string|null} Absolute path inside the pod, or null when neither a directory nor a
|
|
1640
|
+
* known runtime base resolves.
|
|
1641
|
+
* @memberof UnderpostRepository
|
|
1642
|
+
*/
|
|
1643
|
+
runtimeSiteRoot({ runtime, host, routePath = '/', directory } = {}) {
|
|
1644
|
+
const runtimeBase = {
|
|
1645
|
+
wp: `/opt/lampp/htdocs/wp/${host}`,
|
|
1646
|
+
};
|
|
1647
|
+
const vhostDir = directory || runtimeBase[runtime];
|
|
1648
|
+
if (!vhostDir) return null;
|
|
1649
|
+
const subDir = routePath && routePath !== '/' ? routePath.replace(/^\/+/, '').replace(/\/+$/, '') : '';
|
|
1650
|
+
return subDir ? `${vhostDir}/${subDir}` : vhostDir;
|
|
1651
|
+
},
|
|
1652
|
+
|
|
1653
|
+
/**
|
|
1654
|
+
* Probes a running pod for the first candidate directory that is a git repository
|
|
1655
|
+
* (i.e. contains a `.git` entry). Used to locate a site root before backing it up so a
|
|
1656
|
+
* missing/unprovisioned directory is detected up-front instead of producing an opaque
|
|
1657
|
+
* shell `exit 1` from a failed `cd`.
|
|
1658
|
+
*
|
|
1659
|
+
* @param {object} opts
|
|
1660
|
+
* @param {string} opts.podName - Target pod name.
|
|
1661
|
+
* @param {string} opts.namespace - Kubernetes namespace.
|
|
1662
|
+
* @param {string[]} opts.candidates - Absolute paths to probe, most-specific first.
|
|
1663
|
+
* @returns {string|null} The first candidate that is a git repo, or null if none match.
|
|
1664
|
+
* @memberof UnderpostRepository
|
|
1665
|
+
*/
|
|
1666
|
+
podRepoDir({ podName, namespace, candidates }) {
|
|
1667
|
+
const probe = candidates.map((dir) => `if [ -d '${dir}/.git' ]; then echo '${dir}'; exit 0; fi`).join('; ');
|
|
1668
|
+
let out = '';
|
|
1669
|
+
try {
|
|
1670
|
+
out = Underpost.kubectl.exec({ podName, namespace, command: `${probe}; echo ''` }) || '';
|
|
1671
|
+
} catch (err) {
|
|
1672
|
+
logger.warn(`podRepoDir: probe failed in pod ${podName}`, err.message);
|
|
1673
|
+
return null;
|
|
1674
|
+
}
|
|
1675
|
+
return (
|
|
1676
|
+
out
|
|
1677
|
+
.split('\n')
|
|
1678
|
+
.map((line) => line.trim())
|
|
1679
|
+
.find(Boolean) || null
|
|
1680
|
+
);
|
|
1681
|
+
},
|
|
1682
|
+
|
|
1683
|
+
/**
|
|
1684
|
+
* Backs up all repositories defined in a deployment's conf.server.json by executing
|
|
1685
|
+
* git commit+push inside the running deployment pod via `kubectl exec`.
|
|
1686
|
+
*
|
|
1687
|
+
* Scans every `server[host][path]` entry for a `repository` field. For each match
|
|
1688
|
+
* the runtime-specific site root is resolved and a git backup script is executed
|
|
1689
|
+
* inside the pod. GITHUB_TOKEN and GITHUB_USERNAME are injected as ephemeral
|
|
1690
|
+
* environment variables in the exec command — never persisted to the pod filesystem.
|
|
1691
|
+
*
|
|
1692
|
+
* @param {object} opts
|
|
1693
|
+
* @param {string} opts.deployId - Deployment ID (used to read conf.server.json and find pods).
|
|
1694
|
+
* @param {string} [opts.namespace='default'] - Kubernetes namespace.
|
|
1695
|
+
* @param {string} [opts.env='production'] - Deployment environment.
|
|
1696
|
+
* @returns {void}
|
|
1697
|
+
* @memberof UnderpostRepository
|
|
1698
|
+
*/
|
|
1699
|
+
backupPodRepositories({ deployId, namespace = 'default', env = 'production' }) {
|
|
1700
|
+
const confServer = readConfJson(deployId, 'server', { resolve: true });
|
|
1701
|
+
const githubToken = process.env.GITHUB_TOKEN || '';
|
|
1702
|
+
const githubUsername = process.env.GITHUB_USERNAME || 'underpostnet';
|
|
1703
|
+
|
|
1704
|
+
if (!githubToken) {
|
|
1705
|
+
logger.warn('backupPodRepositories: GITHUB_TOKEN not available — git push will fail');
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
// Resolve the active blue/green traffic colour so we target the correct pod
|
|
1709
|
+
const traffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace });
|
|
1710
|
+
if (!traffic) {
|
|
1711
|
+
logger.warn(`backupPodRepositories: could not resolve current traffic for ${deployId} — skipping`);
|
|
1712
|
+
return;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// Find a running pod that matches the active traffic colour
|
|
1716
|
+
const pods = Underpost.kubectl.get(`${deployId}-${env}-${traffic}`, 'pods', namespace);
|
|
1717
|
+
const runningPod = pods.find((p) => p.STATUS === 'Running');
|
|
1718
|
+
if (!runningPod) {
|
|
1719
|
+
logger.warn(`backupPodRepositories: no running ${traffic} pod found for ${deployId} in namespace ${namespace}`);
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
const podName = runningPod.NAME;
|
|
1723
|
+
|
|
1724
|
+
for (const host of Object.keys(confServer)) {
|
|
1725
|
+
for (const routePath of Object.keys(confServer[host])) {
|
|
1726
|
+
const entry = confServer[host][routePath];
|
|
1727
|
+
if (!entry.repository) continue;
|
|
1728
|
+
|
|
1729
|
+
const siteRootArgs = { runtime: entry.runtime, host, directory: entry.directory };
|
|
1730
|
+
const repoRoot = Underpost.repo.runtimeSiteRoot({ ...siteRootArgs, routePath });
|
|
1731
|
+
if (!repoRoot) {
|
|
1732
|
+
logger.warn(`backupPodRepositories: no site-root mapping for runtime '${entry.runtime}' (${host})`);
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// Probe the pod for the actual git repo so an unprovisioned/missing site root is
|
|
1737
|
+
// detected and skipped cleanly instead of failing the in-pod `cd` with exit 1.
|
|
1738
|
+
// Fall back to the vhost dir (root route) to cover conf/path drift.
|
|
1739
|
+
const vhostRoot = Underpost.repo.runtimeSiteRoot({ ...siteRootArgs, routePath: '/' });
|
|
1740
|
+
const candidates = [...new Set([repoRoot, vhostRoot].filter(Boolean))];
|
|
1741
|
+
const siteRoot = Underpost.repo.podRepoDir({ podName, namespace, candidates });
|
|
1742
|
+
if (!siteRoot) {
|
|
1743
|
+
logger.warn(
|
|
1744
|
+
`backupPodRepositories: no git repository found in pod ${podName} for ${host} (checked ${candidates.join(
|
|
1745
|
+
', ',
|
|
1746
|
+
)}) — site may not be provisioned yet; skipping`,
|
|
1747
|
+
);
|
|
1748
|
+
continue;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
const repoName = entry.repository.split('/').pop().split('.')[0];
|
|
1752
|
+
|
|
1753
|
+
// Build the backup script — secrets are injected as env vars in the exec,
|
|
1754
|
+
// never written to filesystem. The shell process inherits them ephemerally.
|
|
1755
|
+
const backupScript = [
|
|
1756
|
+
`export GITHUB_TOKEN='${githubToken.replace(/'/g, "'\\''")}'`,
|
|
1757
|
+
`export GITHUB_USERNAME='${githubUsername.replace(/'/g, "'\\''")}'`,
|
|
1758
|
+
`git config --global --add safe.directory '${siteRoot}' 2>/dev/null || true`,
|
|
1759
|
+
`cd '${siteRoot}' && git add -A && git commit -m 'backup $(date -u +%Y-%m-%dT%H:%M:%SZ)' || true`,
|
|
1760
|
+
`cd '${siteRoot}' && underpost push . ${githubUsername}/${repoName}`,
|
|
1761
|
+
`cd /home/dd/engine && node bin secret underpost --global-clean`,
|
|
1762
|
+
].join(' && ');
|
|
1763
|
+
|
|
1764
|
+
try {
|
|
1765
|
+
logger.info(`backupPodRepositories: backing up ${host} (${entry.runtime}) in pod ${podName}`);
|
|
1766
|
+
Underpost.kubectl.exec({ podName, namespace, command: backupScript });
|
|
1767
|
+
logger.info(`backupPodRepositories: git push done for ${host}`);
|
|
1768
|
+
} catch (err) {
|
|
1769
|
+
logger.error(`backupPodRepositories: backup failed for ${host}`, err.message);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
},
|
|
1774
|
+
|
|
1775
|
+
/**
|
|
1776
|
+
* Clones the deploy-specific private repository into `./engine-private`
|
|
1777
|
+
* when it does not already exist on disk. Returns `{ ephemeral: true }`
|
|
1778
|
+
* If `./engine-private` already exists, the call is a no-op unless
|
|
1779
|
+
* `options.force` is `true`, in which case the directory is removed and
|
|
1780
|
+
* re-cloned.
|
|
1781
|
+
*
|
|
1782
|
+
* @param {string} [deployId] - Deploy ID (e.g. `dd-core`) used to derive
|
|
1783
|
+
* the repo name `engine-{component}-private`. Falls back to
|
|
1784
|
+
* `process.env.DEFAULT_DEPLOY_ID`. When neither is available the
|
|
1785
|
+
* default repo name `engine-private` is used.
|
|
1786
|
+
* @param {object} [options]
|
|
1787
|
+
* @param {boolean} [options.force=false] - Remove existing `engine-private`
|
|
1788
|
+
* and re-clone.
|
|
1789
|
+
* @memberof UnderpostRepository
|
|
1790
|
+
*/
|
|
1791
|
+
privateEngineRepoFactory(deployId, options = { force: false }) {
|
|
1792
|
+
if (fs.existsSync('./engine-private') && !options.force) return;
|
|
1793
|
+
|
|
1794
|
+
if (options.force && fs.existsSync('./engine-private')) {
|
|
1795
|
+
fs.removeSync('./engine-private');
|
|
1796
|
+
logger.info('engine-private removed (force re-clone)');
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
const effectiveDeployId = deployId || process.env.DEFAULT_DEPLOY_ID;
|
|
1800
|
+
|
|
1801
|
+
const username = process.env.GITHUB_USERNAME;
|
|
1802
|
+
if (!username) {
|
|
1803
|
+
throw new Error('privateEngineRepoFactory: GITHUB_USERNAME not set');
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
const repoName = effectiveDeployId ? `engine-${effectiveDeployId.split('-')[1]}-private` : 'engine-private';
|
|
1807
|
+
logger.info(`engine-private missing — cloning ${username}/${repoName}`);
|
|
1808
|
+
shellExec(`underpost clone ${username}/${repoName}`);
|
|
1809
|
+
if (!fs.existsSync(`./${repoName}`)) {
|
|
1810
|
+
throw new Error(`privateEngineRepoFactory: clone failed for ${username}/${repoName}`);
|
|
1811
|
+
}
|
|
1812
|
+
if (repoName !== 'engine-private') shellExec(`mv ./${repoName} ./engine-private`);
|
|
1813
|
+
},
|
|
1814
|
+
|
|
1815
|
+
/**
|
|
1816
|
+
* Removes the ephemeral `engine-private/` clone created by
|
|
1817
|
+
* `privateEngineRepoFactory()`. No-op if the directory does not exist.
|
|
1818
|
+
* @memberof UnderpostRepository
|
|
1819
|
+
*/
|
|
1820
|
+
cleanupPrivateEngineRepo() {
|
|
1821
|
+
if (fs.existsSync('./engine-private')) {
|
|
1822
|
+
fs.removeSync('./engine-private');
|
|
1823
|
+
logger.info('engine-private ephemeral clone removed');
|
|
1824
|
+
}
|
|
1825
|
+
if (fs.existsSync('/home/dd/engine-private')) {
|
|
1826
|
+
fs.removeSync('/home/dd/engine-private');
|
|
1827
|
+
logger.info('engine-private in /home/dd removed');
|
|
1828
|
+
}
|
|
1829
|
+
},
|
|
1830
|
+
|
|
1831
|
+
/**
|
|
1832
|
+
* Resolves the GitHub repository for a given instance runtime by scanning
|
|
1833
|
+
* every `conf.instances.json` listed in `./engine-private/deploy/dd.router`.
|
|
1834
|
+
*
|
|
1835
|
+
* Resolution order:
|
|
1836
|
+
* 1. If `runtime` is falsy, returns `${GITHUB_USERNAME}/engine`.
|
|
1837
|
+
* 2. Iterates each deploy ID found in `dd.router` and looks for an instance
|
|
1838
|
+
* whose `runtime` field matches the supplied value.
|
|
1839
|
+
* 3. When a match is found, returns `instance.metadata.repository`.
|
|
1840
|
+
* 4. Falls back to `${GITHUB_USERNAME}/engine` when no match is found.
|
|
1841
|
+
*
|
|
1842
|
+
* @param {string} [runtime=''] - The runtime identifier to look up (e.g. `'cyberia-server'`, `'cyberia-client'`).
|
|
1843
|
+
* @param {boolean} [ownRuntimeRepo=false] - Whether to check for the runtime's own repository.
|
|
1844
|
+
* @returns {string} The resolved `owner/repo` string.
|
|
1845
|
+
* @memberof UnderpostRepository
|
|
1846
|
+
*/
|
|
1847
|
+
resolveInstanceRepo(runtime = '', ownRuntimeRepo = false) {
|
|
1848
|
+
const fallback = `${process.env.GITHUB_USERNAME}/engine`;
|
|
1849
|
+
if (!runtime) return fallback;
|
|
1850
|
+
// A `.dev` suffix selects the development image workflow
|
|
1851
|
+
// (docker-image.<runtime>.dev.ci.yml) but resolves to the same instance
|
|
1852
|
+
// repo as its production counterpart, so strip it before matching.
|
|
1853
|
+
runtime = runtime.replace(/\.dev$/, '');
|
|
1854
|
+
const ddRouter = './engine-private/deploy/dd.router';
|
|
1855
|
+
const deployIds = fs.existsSync(ddRouter)
|
|
1856
|
+
? fs
|
|
1857
|
+
.readFileSync(ddRouter, 'utf8')
|
|
1858
|
+
.split(',')
|
|
1859
|
+
.map((s) => s.trim())
|
|
1860
|
+
.filter(Boolean)
|
|
1861
|
+
: [];
|
|
1862
|
+
for (const deployId of deployIds) {
|
|
1863
|
+
const confPath = `./engine-private/conf/${deployId}/conf.instances.json`;
|
|
1864
|
+
if (!fs.existsSync(confPath)) continue;
|
|
1865
|
+
try {
|
|
1866
|
+
// readConfInstances returns the bare array of entries. The template
|
|
1867
|
+
// entries carry runtime + metadata.repository, so the un-expanded list
|
|
1868
|
+
// is enough here (no need to expand variants).
|
|
1869
|
+
const match = readConfInstances(deployId).find((i) => i && i.runtime === runtime);
|
|
1870
|
+
if (match && match.metadata && match.metadata.repository) {
|
|
1871
|
+
logger.info(`[resolveInstanceRepo] resolved from ${confPath}`, {
|
|
1872
|
+
runtime,
|
|
1873
|
+
repo: match.metadata.repository,
|
|
1874
|
+
});
|
|
1875
|
+
return match.metadata.repository;
|
|
1876
|
+
}
|
|
1877
|
+
} catch (err) {
|
|
1878
|
+
logger.warn(`[resolveInstanceRepo] failed to parse ${confPath}: ${err.message}`);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
if (ownRuntimeRepo) {
|
|
1882
|
+
const runtimeRepo = Underpost.repo.isRemoteRepo(`${process.env.GITHUB_USERNAME}/${runtime}`);
|
|
1883
|
+
if (runtimeRepo) {
|
|
1884
|
+
logger.info(`[resolveInstanceRepo] resolved from ${process.env.GITHUB_USERNAME}/${runtime}`, {
|
|
1885
|
+
runtime,
|
|
1886
|
+
repo: `${process.env.GITHUB_USERNAME}/${runtime}`,
|
|
1887
|
+
});
|
|
1888
|
+
return `${process.env.GITHUB_USERNAME}/${runtime}`;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
return fallback;
|
|
1892
|
+
},
|
|
1893
|
+
|
|
1894
|
+
/**
|
|
1895
|
+
* Performs a shallow sparse Git checkout of a single subdirectory from any
|
|
1896
|
+
* GitHub repository into a local target directory.
|
|
1897
|
+
*
|
|
1898
|
+
* Uses `--depth 1 --no-checkout` + `git sparse-checkout` so only the
|
|
1899
|
+
* requested path is fetched — no full clone of the remote repo.
|
|
1900
|
+
* Skips the clone entirely when `<targetDir>/<subPath>` already exists on
|
|
1901
|
+
* disk (idempotent).
|
|
1902
|
+
*
|
|
1903
|
+
* Requires `GITHUB_TOKEN` to be set in the environment for authenticated
|
|
1904
|
+
* access to private repositories.
|
|
1905
|
+
*
|
|
1906
|
+
* @param {string} subPath - The subdirectory path within the remote repo to
|
|
1907
|
+
* check out (e.g. `'conf/dd-prototype'`, `'src/api/payments'`).
|
|
1908
|
+
* @param {object} [options]
|
|
1909
|
+
* @param {string} [options.repoOwner='underpostnet'] - GitHub organisation or
|
|
1910
|
+
* user that owns the repository.
|
|
1911
|
+
* @param {string} [options.repoName='engine-private'] - Name of the
|
|
1912
|
+
* repository on GitHub.
|
|
1913
|
+
* @param {string} [options.targetDir='./engine-private'] - Local directory
|
|
1914
|
+
* where the repo will be cloned.
|
|
1915
|
+
* @returns {boolean} `true` when the checkout was performed, `false` when it
|
|
1916
|
+
* was skipped because the target path already existed.
|
|
1917
|
+
* @memberof UnderpostRepository
|
|
1918
|
+
*/
|
|
1919
|
+
sparseCheckoutDirectory(
|
|
1920
|
+
subPath,
|
|
1921
|
+
options = { repoOwner: 'underpostnet', repoName: 'engine-private', targetDir: './engine-private' },
|
|
1922
|
+
) {
|
|
1923
|
+
const { repoOwner = 'underpostnet', repoName = 'engine-private', targetDir = './engine-private' } = options;
|
|
1924
|
+
const localPath = `${targetDir}/${subPath}`;
|
|
1925
|
+
if (fs.existsSync(localPath)) {
|
|
1926
|
+
logger.info('[sparseCheckoutDirectory] path already present, skipping', localPath);
|
|
1927
|
+
return false;
|
|
1928
|
+
}
|
|
1929
|
+
const authUrl = `https://${process.env.GITHUB_TOKEN}@github.com/${repoOwner}/${repoName}.git`;
|
|
1930
|
+
shellExec(`git clone --depth 1 --no-checkout ${authUrl} ${targetDir}`, { disableLog: true });
|
|
1931
|
+
shellExec(`cd ${targetDir} && git sparse-checkout set ${subPath} && git checkout`, { disableLog: true });
|
|
1932
|
+
logger.info('[sparseCheckoutDirectory] sparse checkout complete', localPath);
|
|
1933
|
+
return true;
|
|
1934
|
+
},
|
|
1935
|
+
|
|
1936
|
+
/**
|
|
1937
|
+
* Ensures a deploy's public source repo (e.g. `engine-prototype`) is present
|
|
1938
|
+
* next to the engine and reset to a pristine HEAD, so catalog `sourceMoves`
|
|
1939
|
+
* can (re)pull custom sources even after a previous build moved them out of
|
|
1940
|
+
* the source tree.
|
|
1941
|
+
*
|
|
1942
|
+
* Clones `../<repoName>` when missing; otherwise restores a clean checkout
|
|
1943
|
+
* (`git checkout .` brings back any moved-out tracked files) and pulls latest.
|
|
1944
|
+
* Mirrors the sibling-repo handling used by `syncPrivateConf`.
|
|
1945
|
+
*
|
|
1946
|
+
* @param {string} repoName - Public source repo name (e.g. `engine-prototype`).
|
|
1947
|
+
* @returns {boolean} `true` when the repo is available on disk.
|
|
1948
|
+
* @memberof UnderpostRepository
|
|
1949
|
+
*/
|
|
1950
|
+
pullSourceRepo(repoName) {
|
|
1951
|
+
const username = process.env.GITHUB_USERNAME;
|
|
1952
|
+
if (!username || !repoName) return false;
|
|
1953
|
+
const repoPath = `../${repoName}`;
|
|
1954
|
+
const gitUri = `${username}/${repoName}`;
|
|
1955
|
+
if (!fs.existsSync(repoPath)) {
|
|
1956
|
+
shellExec(`cd .. && underpost clone ${gitUri}`, { silent: true });
|
|
1957
|
+
} else {
|
|
1958
|
+
const repoAbsPath = path.resolve(repoPath);
|
|
1959
|
+
shellExec(`git config --global --add safe.directory '${repoAbsPath}'`);
|
|
1960
|
+
shellExec(`cd ${repoPath} && git checkout . && git clean -f -d && underpost pull . ${gitUri}`, {
|
|
1961
|
+
silent: true,
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
return fs.existsSync(repoPath);
|
|
1965
|
+
},
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
export default UnderpostRepository;
|