@underpostnet/cyberia 3.2.90 → 3.3.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (490) hide show
  1. package/.dockerignore +1 -0
  2. package/.env.example +16 -0
  3. package/.github/workflows/coverall.cyberia.ci.yml +107 -0
  4. package/.github/workflows/cyberia-client.cd.yml +6 -18
  5. package/.github/workflows/cyberia-server.cd.yml +6 -18
  6. package/.github/workflows/docker-image.cyberia-client.ci.yml +8 -5
  7. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +8 -3
  8. package/.github/workflows/docker-image.cyberia-server.ci.yml +8 -5
  9. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +8 -5
  10. package/.github/workflows/docker-image.engine-cyberia.ci.yml +15 -1
  11. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +15 -1
  12. package/.github/workflows/engine-cyberia.cd.yml +6 -78
  13. package/.github/workflows/engine-cyberia.ci.yml +4 -5
  14. package/.github/workflows/ghpkg.ci.yml +42 -21
  15. package/.github/workflows/hardhat.ci.yml +12 -11
  16. package/.github/workflows/npmpkg.ci.yml +17 -15
  17. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -16
  18. package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
  19. package/.github/workflows/release.cd.yml +2 -9
  20. package/.prettierignore +1 -0
  21. package/AGENTS.md +47 -0
  22. package/CHANGELOG.md +581 -2417
  23. package/CLI-HELP.md +416 -127
  24. package/Dockerfile +25 -9
  25. package/Dockerfile.dev +25 -9
  26. package/Dockerfile.test +4 -4
  27. package/README.md +21 -1
  28. package/bin/build.js +70 -43
  29. package/bin/build.template.js +8 -3
  30. package/bin/cyberia.js +372 -135
  31. package/bin/deploy.js +18 -104
  32. package/bin/index.js +372 -135
  33. package/compose.env +16 -0
  34. package/conf.js +93 -4
  35. package/deploy/cyberia-client/deploy.sh +47 -0
  36. package/deploy/cyberia-client/package.sh +18 -0
  37. package/deploy/cyberia-client/state.sh +14 -0
  38. package/deploy/cyberia-server/deploy.sh +47 -0
  39. package/deploy/cyberia-server/package.sh +18 -0
  40. package/deploy/cyberia-server/state.sh +14 -0
  41. package/deploy/dd-cyberia/deploy.sh +22 -0
  42. package/deploy/dd-cyberia/init.sh +55 -0
  43. package/deploy/dd-cyberia/package.sh +17 -0
  44. package/deploy/dd-cyberia/state.sh +14 -0
  45. package/deploy/dd-cyberia/sync-deploy.sh +116 -0
  46. package/deploy/lib/github-actions-logging.sh +594 -0
  47. package/deploy/lib/host.sh +185 -0
  48. package/deploy/lib/state.sh +62 -0
  49. package/deploy/pwa-microservices-template/deploy.sh +45 -0
  50. package/deploy/release/deploy.sh +32 -0
  51. package/deployment.yaml +7 -2
  52. package/docker-compose.yml +31 -37
  53. package/examples/static-page/README.md +19 -19
  54. package/examples/static-page/ssr-components/CustomPage.js +2 -2
  55. package/hardhat/hardhat.config.js +1 -6
  56. package/hardhat/package-lock.json +104 -43
  57. package/hardhat/package.json +3 -3
  58. package/manifests/alertmanager/deployment.yaml +81 -0
  59. package/manifests/blackbox-exporter/deployment.yaml +75 -0
  60. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +36 -7
  61. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +77 -0
  62. package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
  63. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  64. package/manifests/grafana/deployment.yaml +26 -7
  65. package/manifests/grafana/kustomization.yaml +1 -0
  66. package/manifests/grafana/service.yaml +6 -1
  67. package/manifests/mariadb/pv.yaml +1 -0
  68. package/manifests/mongodb/statefulset.yaml +9 -2
  69. package/manifests/mysql/pv-pvc.yaml +14 -1
  70. package/manifests/postgresql/pv.yaml +12 -1
  71. package/manifests/postgresql/pvc.yaml +1 -0
  72. package/manifests/prometheus/deployment.yaml +18 -22
  73. package/package.json +83 -10
  74. package/scripts/audit-selinux.sh +64 -0
  75. package/scripts/event-monitor.sh +56 -0
  76. package/scripts/gpu-diag.sh +0 -0
  77. package/scripts/ip-info.sh +0 -0
  78. package/scripts/k3s-node-setup.sh +30 -14
  79. package/scripts/kubeadm-node-setup.sh +40 -31
  80. package/scripts/lxd-vm-setup.sh +0 -0
  81. package/scripts/maas-nat-firewalld.sh +0 -0
  82. package/scripts/maas-setup.sh +8 -8
  83. package/scripts/nat-iptables.sh +2 -0
  84. package/scripts/rhel-grpc-setup.sh +0 -0
  85. package/scripts/rocky-kickstart.sh +25 -9
  86. package/scripts/selinux-normalize.sh +907 -0
  87. package/scripts/shutdown-machine.sh +843 -0
  88. package/scripts/test-monitor.sh +3 -3
  89. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +1 -1
  90. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +2 -2
  91. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +2 -2
  92. package/src/api/core/core.controller.js +1 -1
  93. package/src/api/core/core.router.js +2 -2
  94. package/src/api/core/core.service.js +2 -2
  95. package/src/api/crypto/crypto.controller.js +1 -1
  96. package/src/api/crypto/crypto.service.js +1 -1
  97. package/src/api/cyberia-action/cyberia-action.controller.js +1 -1
  98. package/src/api/cyberia-action/cyberia-action.router.js +1 -1
  99. package/src/api/cyberia-action/cyberia-action.service.js +2 -2
  100. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -1
  101. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +1 -1
  102. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +2 -2
  103. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
  104. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
  105. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
  106. package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
  107. package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
  108. package/src/api/cyberia-entity/cyberia-entity.service.js +2 -2
  109. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +1 -1
  110. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +1 -1
  111. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +2 -2
  112. package/src/api/cyberia-instance/cyberia-fallback-capture.js +466 -0
  113. package/src/api/cyberia-instance/cyberia-fallback-default-items.js +63 -0
  114. package/src/api/cyberia-instance/cyberia-fallback-world.js +16 -1
  115. package/src/api/cyberia-instance/cyberia-instance-map.service.js +1 -1
  116. package/src/api/cyberia-instance/cyberia-instance.controller.js +4 -1
  117. package/src/api/cyberia-instance/cyberia-instance.router.js +15 -2
  118. package/src/api/cyberia-instance/cyberia-instance.service.js +45 -2
  119. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
  120. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +1 -0
  121. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
  122. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +2 -2
  123. package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
  124. package/src/api/cyberia-map/cyberia-map.router.js +1 -1
  125. package/src/api/cyberia-map/cyberia-map.service.js +2 -2
  126. package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
  127. package/src/api/cyberia-quest/cyberia-quest.router.js +1 -1
  128. package/src/api/cyberia-quest/cyberia-quest.service.js +2 -2
  129. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
  130. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
  131. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
  132. package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
  133. package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
  134. package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
  135. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +16 -29
  136. package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
  137. package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
  138. package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
  139. package/src/api/default/default.controller.js +1 -1
  140. package/src/api/default/default.router.js +1 -1
  141. package/src/api/default/default.service.js +2 -2
  142. package/src/api/document/document.controller.js +1 -1
  143. package/src/api/document/document.router.js +1 -1
  144. package/src/api/document/document.service.js +2 -2
  145. package/src/api/file/file.controller.js +1 -1
  146. package/src/api/file/file.router.js +1 -1
  147. package/src/api/file/file.service.js +2 -2
  148. package/src/api/instance/instance.controller.js +1 -1
  149. package/src/api/instance/instance.router.js +1 -1
  150. package/src/api/instance/instance.service.js +2 -2
  151. package/src/api/ipfs/ipfs.controller.js +1 -1
  152. package/src/api/ipfs/ipfs.router.js +2 -2
  153. package/src/api/ipfs/ipfs.service.js +2 -2
  154. package/src/api/object-layer/object-layer.controller.js +1 -1
  155. package/src/api/object-layer/object-layer.model.js +204 -3
  156. package/src/api/object-layer/object-layer.router.js +2 -2
  157. package/src/api/object-layer/object-layer.service.js +4 -17
  158. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
  159. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
  160. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
  161. package/src/api/test/test.controller.js +1 -1
  162. package/src/api/test/test.service.js +1 -1
  163. package/src/api/user/guest.service.js +3 -3
  164. package/src/api/user/user.controller.js +1 -1
  165. package/src/api/user/user.router.js +2 -2
  166. package/src/api/user/user.service.js +4 -4
  167. package/src/api.js +5 -9
  168. package/src/cli/app.js +391 -0
  169. package/src/cli/baremetal.js +30 -36
  170. package/src/cli/client.js +306 -0
  171. package/src/cli/cloud-init.js +3 -3
  172. package/src/cli/cluster.js +242 -135
  173. package/src/cli/db.js +126 -67
  174. package/src/cli/deploy.js +158 -60
  175. package/src/cli/docker-compose.js +5 -5
  176. package/src/cli/domains.js +184 -0
  177. package/src/cli/dotenv-store.js +143 -0
  178. package/src/cli/event.js +2281 -0
  179. package/src/cli/fs.js +3 -3
  180. package/src/cli/host.js +672 -0
  181. package/src/cli/image.js +4 -7
  182. package/src/cli/index.js +426 -134
  183. package/src/cli/ipfs.js +34 -30
  184. package/src/cli/kickstart.js +1 -1
  185. package/src/cli/kubectl.js +218 -41
  186. package/src/cli/lxd.js +3 -3
  187. package/src/cli/monitor.js +1359 -26
  188. package/src/cli/package.js +90 -0
  189. package/src/cli/release.js +73 -34
  190. package/src/cli/repository.js +564 -367
  191. package/src/cli/run.js +362 -654
  192. package/src/cli/secrets.js +2158 -964
  193. package/src/cli/ssh.js +651 -331
  194. package/src/cli/state.js +394 -0
  195. package/src/cli/static.js +2 -2
  196. package/src/cli/system.js +26 -13
  197. package/src/cli/test.js +199 -125
  198. package/src/cli/vultr.js +623 -0
  199. package/src/cli/wireguard.js +2930 -0
  200. package/src/client/components/core/CalendarCore.js +53 -96
  201. package/src/client/components/core/Docs.js +258 -211
  202. package/src/client/components/core/FileExplorer.js +0 -21
  203. package/src/client/components/core/PanelForm.js +4 -4
  204. package/src/client/components/core/Repository.js +56 -0
  205. package/src/client/components/core/Translate.js +7 -0
  206. package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
  207. package/src/client/components/core/Wallet.js +0 -11
  208. package/src/client/components/core/Worker.js +7 -1
  209. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -2
  210. package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +368 -0
  211. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +35 -1
  212. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +4 -0
  213. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
  214. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
  215. package/src/client/components/default/AppShellDefault.js +1 -1
  216. package/src/client/components/default/SettingsDefault.js +2 -2
  217. package/src/client/components/itemledger/SettingsItemledger.js +2 -2
  218. package/src/client/components/underpost/AppShellUnderpost.js +3 -2
  219. package/src/client/components/underpost/SettingsUnderpost.js +2 -2
  220. package/src/client/public/cyberia-docs/ARCHITECTURE.md +17 -3
  221. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +32 -5
  222. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +1 -1
  223. package/src/client/public/cyberia-docs/CYBERIA.md +21 -1
  224. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
  225. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  226. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  227. package/src/client/public/default/android-chrome-144x144.png +0 -0
  228. package/src/client/public/default/android-chrome-192x192.png +0 -0
  229. package/src/client/public/default/android-chrome-256x256.png +0 -0
  230. package/src/client/public/default/android-chrome-36x36.png +0 -0
  231. package/src/client/public/default/android-chrome-384x384.png +0 -0
  232. package/src/client/public/default/android-chrome-48x48.png +0 -0
  233. package/src/client/public/default/android-chrome-512x512.png +0 -0
  234. package/src/client/public/default/android-chrome-72x72.png +0 -0
  235. package/src/client/public/default/android-chrome-96x96.png +0 -0
  236. package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
  237. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  238. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  239. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  240. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  241. package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
  242. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  243. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  244. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  245. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  246. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  247. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  248. package/src/client/public/default/apple-touch-icon.png +0 -0
  249. package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
  250. package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
  251. package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
  252. package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
  253. package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
  254. package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
  255. package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
  256. package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
  257. package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
  258. package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
  259. package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
  260. package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
  261. package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
  262. package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
  263. package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
  264. package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
  265. package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
  266. package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
  267. package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
  268. package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
  269. package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
  270. package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
  271. package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
  272. package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
  273. package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
  274. package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
  275. package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
  276. package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
  277. package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
  278. package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
  279. package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
  280. package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
  281. package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
  282. package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
  283. package/src/client/public/default/assets/background/white.jpg +0 -0
  284. package/src/client/public/default/browserconfig.xml +1 -1
  285. package/src/client/public/default/favicon-16x16.png +0 -0
  286. package/src/client/public/default/favicon-32x32.png +0 -0
  287. package/src/client/public/default/favicon-48x48.png +0 -0
  288. package/src/client/public/default/favicon.ico +0 -0
  289. package/src/client/public/default/manifest.webmanifest +5 -5
  290. package/src/client/public/default/mstile-144x144.png +0 -0
  291. package/src/client/public/default/mstile-150x150.png +0 -0
  292. package/src/client/public/default/mstile-310x150.png +0 -0
  293. package/src/client/public/default/mstile-310x310.png +0 -0
  294. package/src/client/public/default/mstile-70x70.png +0 -0
  295. package/src/client/public/default/yandex-browser-50x50.png +0 -0
  296. package/src/client/public/default/yandex-browser-manifest.json +1 -1
  297. package/src/client/services/cyberia-instance/cyberia-instance.service.js +40 -0
  298. package/src/client/services/object-layer/object-layer.management.js +4 -4
  299. package/src/client/ssr/RootDocument.js +1 -1
  300. package/src/client/ssr/body/404.js +1 -1
  301. package/src/client/ssr/body/500.js +1 -1
  302. package/src/client/ssr/body/CacheControl.js +1 -1
  303. package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
  304. package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
  305. package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
  306. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
  307. package/src/client/ssr/head/CryptokoynScripts.js +1 -1
  308. package/src/client/ssr/head/Css.js +1 -1
  309. package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
  310. package/src/client/ssr/head/DefaultScripts.js +1 -1
  311. package/src/client/ssr/head/ItemledgerScripts.js +1 -1
  312. package/src/client/ssr/head/Microdata.js +1 -1
  313. package/src/client/ssr/head/Production.js +1 -1
  314. package/src/client/ssr/head/Pwa.js +1 -1
  315. package/src/client/ssr/head/PwaItemledger.js +1 -1
  316. package/src/client/ssr/head/Seo.js +1 -1
  317. package/src/client/ssr/head/UnderpostScripts.js +1 -1
  318. package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
  319. package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
  320. package/src/client/ssr/views/Cyberia404.js +7 -5
  321. package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
  322. package/src/client/ssr/views/Maintenance.js +1 -1
  323. package/src/client/ssr/views/NoNetworkConnection.js +1 -1
  324. package/src/client/ssr/views/Test.js +1 -1
  325. package/src/client/sw/core.sw.js +20 -14
  326. package/src/client-builder/client-build-docs.js +61 -46
  327. package/src/client-builder/client-build-live.js +2 -2
  328. package/src/client-builder/client-build.js +58 -44
  329. package/src/client-builder/client-dev-server.js +3 -3
  330. package/src/client-builder/client-icons.js +2 -2
  331. package/src/client-builder/ssr.js +3 -3
  332. package/src/client.build.js +3 -6
  333. package/src/client.dev.js +3 -6
  334. package/src/db/DataBaseProvider.js +186 -3
  335. package/src/db/mariadb/MariaDB.js +3 -2
  336. package/src/db/mongo/MongoBootstrap.js +273 -101
  337. package/src/db/mongo/MongooseDB.js +11 -8
  338. package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
  339. package/src/grpc/cyberia/grpc-server.js +1 -1
  340. package/src/index.js +103 -23
  341. package/src/mailer/EmailRender.js +2 -2
  342. package/src/mailer/MailerInterceptor.js +87 -0
  343. package/src/mailer/MailerProvider.js +50 -6
  344. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +1 -1
  345. package/src/projects/cyberia/besu-genesis-generator.js +3 -3
  346. package/src/projects/cyberia/catalog-cyberia.js +54 -6
  347. package/src/projects/cyberia/gemini-client.js +1 -1
  348. package/src/projects/cyberia/generate-saga.js +15 -24
  349. package/src/projects/cyberia/hot-reload-trigger.js +1 -1
  350. package/src/projects/cyberia/instance-data.js +22 -3
  351. package/src/projects/cyberia/ipfs-client.js +4 -4
  352. package/src/projects/cyberia/map-preview-generator.js +1 -1
  353. package/src/projects/cyberia/object-layer.js +12 -22
  354. package/src/projects/cyberia/semantic-layer-generator.js +1 -1
  355. package/src/projects/underpost/catalog-underpost.js +13 -2
  356. package/src/proxy.js +4 -7
  357. package/src/runtime/cyberia-client/Dockerfile +9 -4
  358. package/src/runtime/cyberia-client/Dockerfile.dev +8 -2
  359. package/src/runtime/cyberia-server/Dockerfile +9 -4
  360. package/src/runtime/cyberia-server/Dockerfile.dev +8 -2
  361. package/src/runtime/engine-cyberia/Dockerfile +25 -9
  362. package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
  363. package/src/runtime/engine-cyberia/Dockerfile.test +4 -4
  364. package/src/runtime/engine-cyberia/compose.env +16 -0
  365. package/src/runtime/engine-cyberia/docker-compose.yml +31 -37
  366. package/src/runtime/express/Express.js +32 -27
  367. package/src/runtime/lampp/Lampp.js +22 -70
  368. package/src/runtime/nginx/Nginx.js +1 -1
  369. package/src/runtime/wp/Wp.js +16 -48
  370. package/src/server/{catalog.js → build/catalog.js} +15 -5
  371. package/src/server/build/coverage.js +117 -0
  372. package/src/server/build/execution.js +402 -0
  373. package/src/server/build/package.js +465 -0
  374. package/src/server/build/testing.js +528 -0
  375. package/src/server/{dns.js → network/dns.js} +241 -14
  376. package/src/server/network/forward-proxy.js +541 -0
  377. package/src/server/{middlewares.js → network/middlewares.js} +59 -4
  378. package/src/server/network/node-capability.js +98 -0
  379. package/src/server/{peer.js → network/peer.js} +3 -3
  380. package/src/server/{proxy.js → network/proxy.js} +7 -13
  381. package/src/server/network/router.js +356 -0
  382. package/src/server/{tls.js → network/tls.js} +2 -2
  383. package/src/server/network/underpost-compression.js +186 -0
  384. package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +26 -16
  385. package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +19 -3
  386. package/src/server/{cri.js → ops/cri.js} +2 -2
  387. package/src/server/ops/cron.js +949 -0
  388. package/src/server/ops/event-notification.js +284 -0
  389. package/src/server/{logger.js → ops/logger.js} +108 -23
  390. package/src/server/ops/monitoring.js +1724 -0
  391. package/src/server/ops/systemd.js +259 -0
  392. package/src/server/{conf.js → runtime/conf.js} +521 -520
  393. package/src/server/runtime/config-scope.js +215 -0
  394. package/src/server/runtime/environment.js +149 -0
  395. package/src/server/{process.js → runtime/process.js} +153 -13
  396. package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
  397. package/src/server/{runtime.js → runtime/runtime.js} +12 -8
  398. package/src/server/{start.js → runtime/start.js} +128 -39
  399. package/src/server/{auth.js → security/auth.js} +7 -8
  400. package/src/server/security/container-storage.js +382 -0
  401. package/src/server/{crypto.js → security/crypto.js} +1 -1
  402. package/src/server/security/selinux.js +185 -0
  403. package/src/server/{backup.js → storage/backup.js} +17 -8
  404. package/src/server/{data-query.js → storage/data-query.js} +1 -1
  405. package/src/server/{downloader.js → storage/downloader.js} +2 -2
  406. package/src/server/storage/repository.js +68 -0
  407. package/src/server.js +4 -7
  408. package/src/ws/IoInterface.js +1 -1
  409. package/src/ws/IoServer.js +1 -1
  410. package/src/ws/core/core.ws.connection.js +1 -1
  411. package/src/ws/core/core.ws.emit.js +1 -1
  412. package/src/ws/core/core.ws.server.js +1 -1
  413. package/src/ws/default/default.ws.connection.js +1 -1
  414. package/src/ws/default/default.ws.emit.js +1 -1
  415. package/src/ws/default/default.ws.server.js +1 -1
  416. package/test/e2e/event-e2e-public-ingress-down.js +45 -0
  417. package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
  418. package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
  419. package/test/{api.test.js → integration/app/api.test.js} +33 -14
  420. package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +30 -0
  421. package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +2 -1
  422. package/test/integration/app/cyberia/cyberia-load.test.js +467 -0
  423. package/test/integration/app/cyberia/fallback-world-capture.test.js +253 -0
  424. package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
  425. package/test/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
  426. package/test/integration/infra/1-security/config-scope.test.js +305 -0
  427. package/test/integration/infra/1-security/container-storage.test.js +356 -0
  428. package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
  429. package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
  430. package/test/integration/infra/1-security/selinux.test.js +69 -0
  431. package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
  432. package/test/integration/infra/1-security/systemd-service.test.js +70 -0
  433. package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
  434. package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
  435. package/test/integration/infra/2-network/node-capability.test.js +182 -0
  436. package/test/integration/infra/2-network/wireguard-cli.test.js +1163 -0
  437. package/test/integration/infra/2-network/wireguard-edge.test.js +1731 -0
  438. package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
  439. package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
  440. package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
  441. package/test/integration/infra/4-ingress/deploy-routes.test.js +379 -0
  442. package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
  443. package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
  444. package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +47 -6
  445. package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +56 -4
  446. package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
  447. package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
  448. package/test/integration/infra/5-observability/event-notification.test.js +197 -0
  449. package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
  450. package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
  451. package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
  452. package/test/support/shell-harness.js +73 -0
  453. package/test/unit/build-template.test.js +97 -0
  454. package/test/unit/build-workflow-offline.test.js +132 -0
  455. package/test/unit/catalog.test.js +71 -0
  456. package/test/unit/client-build-docs.test.js +334 -0
  457. package/test/unit/conf-loading.test.js +303 -0
  458. package/test/unit/conf-resolution.test.js +979 -0
  459. package/test/unit/coverage-artifact.test.js +130 -0
  460. package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
  461. package/test/unit/deploy-env-resolution.test.js +38 -0
  462. package/test/unit/deploy-log-table.test.js +447 -0
  463. package/test/unit/execution-profiles.test.js +219 -0
  464. package/test/unit/fleet-sync-source.test.js +149 -0
  465. package/test/unit/logger-redaction.test.js +112 -0
  466. package/test/unit/package.test.js +403 -0
  467. package/test/unit/prepare-host.test.js +299 -0
  468. package/test/unit/process-environment.test.js +18 -0
  469. package/test/unit/propagation-message.test.js +78 -0
  470. package/test/unit/redeploy-plan.test.js +24 -0
  471. package/test/unit/release-bump.test.js +474 -0
  472. package/test/unit/ssh-output-redaction.test.js +28 -0
  473. package/test/unit/start-options.test.js +46 -0
  474. package/test/unit/test-tiers.test.js +193 -0
  475. package/vitest.config.js +58 -0
  476. package/.nycrc +0 -9
  477. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
  478. package/manifests/mongodb/pv-pvc.yaml +0 -59
  479. package/scripts/link-local-underpost-cli.sh +0 -6
  480. package/src/api/user/user.build.js +0 -16
  481. package/src/cli/env.js +0 -177
  482. package/src/client/components/core/Blockchain.js +0 -41
  483. package/src/client/components/core/Blog.js +0 -9
  484. package/src/client/components/core/KeyboardAvoidance.js +0 -145
  485. package/src/client/public/default/assets/background/dark.jpg +0 -0
  486. package/src/client/public/default/assets/background/white0-min.jpg +0 -0
  487. package/src/client/public/default/assets/background/white0.jpg +0 -0
  488. package/src/client/public/doc/favicon.ico +0 -0
  489. package/src/client/public/doc/sitemap +0 -148
  490. package/src/server/cron.js +0 -476
@@ -15,9 +15,9 @@ import {
15
15
  loadConfInstances,
16
16
  loadProjectInstanceEnvBuilder,
17
17
  normalizeInstanceTopology,
18
- } from '../src/server/conf.js';
19
- import { statusPageAssetPathFactory } from '../src/server/underpost-gateway.js';
20
- import UnderpostDockerCompose from '../src/cli/docker-compose.js';
18
+ } from '../../../../src/server/runtime/conf.js';
19
+ import { statusPageAssetPathFactory } from '../../../../src/server/network/underpost-gateway.js';
20
+ import UnderpostDockerCompose from '../../../../src/cli/docker-compose.js';
21
21
 
22
22
  // `clusterInstancesFactory` reads `./engine-private/conf/<deployId>/conf.instances.json`
23
23
  // relative to the process cwd, mirroring every other conf loader. engine-private
@@ -61,7 +61,7 @@ const FIXTURES = {
61
61
  describe('cluster custom instances', () => {
62
62
  const created = [];
63
63
 
64
- before(() => {
64
+ beforeAll(() => {
65
65
  for (const [deployId, entries] of Object.entries(FIXTURES)) {
66
66
  const dir = CONF_DIR(deployId);
67
67
  if (fs.existsSync(dir)) throw new Error(`Refusing to write fixtures into an existing deploy: ${dir}`);
@@ -75,7 +75,7 @@ describe('cluster custom instances', () => {
75
75
  fs.outputFileSync(`${customComposeDir}/project-router.conf`, 'project-owned\n');
76
76
  });
77
77
 
78
- after(() => {
78
+ afterAll(() => {
79
79
  for (const dir of created) fs.removeSync(dir);
80
80
  });
81
81
 
@@ -158,12 +158,8 @@ describe('cluster custom instances', () => {
158
158
  const hostsPath = `${customComposeDir}/hosts`;
159
159
  fs.writeFileSync(hostsPath, '127.0.0.1 localhost\n', 'utf8');
160
160
  const options = { path: hostsPath, append: true, blockId: 'fixture-docker-compose' };
161
- expect(etcHostFactory(['fixture-client', 'fixture-server', 'fixture-engine'], options).changed).to.equal(
162
- true,
163
- );
164
- expect(etcHostFactory(['fixture-client', 'fixture-server', 'fixture-engine'], options).changed).to.equal(
165
- false,
166
- );
161
+ expect(etcHostFactory(['fixture-client', 'fixture-server', 'fixture-engine'], options).changed).to.equal(true);
162
+ expect(etcHostFactory(['fixture-client', 'fixture-server', 'fixture-engine'], options).changed).to.equal(false);
167
163
  const hosts = fs.readFileSync(hostsPath, 'utf8');
168
164
  expect(hosts.match(/underpost hosts fixture-docker-compose:begin/g)).to.have.length(1);
169
165
  expect(hosts).to.include('127.0.0.1 localhost');
@@ -184,7 +180,7 @@ describe('cluster custom instances', () => {
184
180
  it('resolves several instances across several deploys', () => {
185
181
  const { byDeployId, unmatched } = clusterInstancesFactory(
186
182
  ['dd-fixture-a', 'dd-fixture-b'],
187
- 'mmo-server+mmo-client+worker',
183
+ 'mmo-server,mmo-client,worker',
188
184
  );
189
185
  expect(byDeployId['dd-fixture-a'].ids).to.deep.equal(['mmo-server', 'mmo-client']);
190
186
  expect(byDeployId['dd-fixture-a'].hosts).to.deep.equal(['server.fixture.test', 'client.fixture.test']);
@@ -207,13 +203,13 @@ describe('cluster custom instances', () => {
207
203
  });
208
204
 
209
205
  it('reports an id no deploy declares instead of silently skipping it', () => {
210
- const { byDeployId, unmatched } = clusterInstancesFactory(['dd-fixture-a'], 'mmo-server+nope');
206
+ const { byDeployId, unmatched } = clusterInstancesFactory(['dd-fixture-a'], 'mmo-server,nope');
211
207
  expect(byDeployId['dd-fixture-a'].ids).to.deep.equal(['mmo-server']);
212
208
  expect(unmatched).to.deep.equal(['nope']);
213
209
  });
214
210
 
215
211
  it('is a no-op when no instance list is given', () => {
216
- for (const list of ['', undefined, '+'])
212
+ for (const list of ['', undefined, ','])
217
213
  expect(clusterInstancesFactory(['dd-fixture-a'], list)).to.deep.equal({
218
214
  byDeployId: { 'dd-fixture-a': { ids: [], hosts: [] } },
219
215
  unmatched: [],
@@ -311,7 +307,7 @@ describe('cluster custom instances', () => {
311
307
  });
312
308
 
313
309
  describe('cluster gateway bootstrap order', () => {
314
- const source = fs.readFileSync(new URL('../src/cli/run.js', import.meta.url), 'utf8');
310
+ const source = fs.readFileSync(new URL('../../../../src/cli/run.js', import.meta.url), 'utf8');
315
311
  const clusterRunner = source.slice(source.indexOf('cluster: async'), source.indexOf("'gateway-status': async"));
316
312
 
317
313
  it('tests the ingress-only fallback before applying any workload Deployment', () => {
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { expect } from 'chai';
4
4
  import fs from 'fs-extra';
5
- import Underpost from '../src/index.js';
5
+ import Underpost from '../../../../src/index.js';
6
6
 
7
7
  const deploymentManifest = (nodeName = '') =>
8
8
  Underpost.deploy.deploymentYamlPartsFactory({
@@ -29,7 +29,7 @@ describe('deployment node placement', () => {
29
29
  });
30
30
 
31
31
  it('does not restart a controller after node-move patches its pod template', () => {
32
- const source = fs.readFileSync(new URL('../src/cli/run.js', import.meta.url), 'utf8');
32
+ const source = fs.readFileSync(new URL('../../../../src/cli/run.js', import.meta.url), 'utf8');
33
33
  const start = source.indexOf(" 'node-move':");
34
34
  const end = source.indexOf('\n /**', start);
35
35
  const runner = source.slice(start, end);
@@ -38,7 +38,7 @@ describe('deployment node placement', () => {
38
38
  });
39
39
 
40
40
  it('forwards --node-name into live and generated custom-instance manifests', () => {
41
- const source = fs.readFileSync(new URL('../src/cli/run.js', import.meta.url), 'utf8');
41
+ const source = fs.readFileSync(new URL('../../../../src/cli/run.js', import.meta.url), 'utf8');
42
42
  const placements = source.match(/nodeName: options\.nodeName\s*\? Underpost\.deploy\.resolveDeployNode/g) || [];
43
43
  expect(placements).to.have.length(2);
44
44
  });
@@ -0,0 +1,313 @@
1
+ 'use strict';
2
+
3
+ import { expect } from 'chai';
4
+ import fs from 'fs-extra';
5
+ import UnderpostDockerCompose from '../../../../src/cli/docker-compose.js';
6
+ import { shellHarness } from '../../../support/shell-harness.js';
7
+
8
+ // Everything below writes generated artifacts under the engine tree and drives
9
+ // the docker CLI. Both are replaced: an in-memory file table for the artifacts,
10
+ // and the shell harness for the compose invocations.
11
+ const composeFixture = (files = {}) => {
12
+ const table = new Map(Object.entries(files));
13
+ const written = new Map();
14
+ const removed = [];
15
+ const keys = () => [...table.keys(), ...written.keys()];
16
+
17
+ vi.spyOn(fs, 'existsSync').mockImplementation((filePath) => {
18
+ const key = `${filePath}`;
19
+ if (removed.includes(key)) return false;
20
+ return table.has(key) || written.has(key) || keys().some((entry) => entry.startsWith(`${key}/`));
21
+ });
22
+ vi.spyOn(fs, 'readFileSync').mockImplementation(
23
+ (filePath) => table.get(`${filePath}`) ?? written.get(`${filePath}`) ?? '',
24
+ );
25
+ vi.spyOn(fs, 'writeFileSync').mockImplementation((filePath, value) => written.set(`${filePath}`, `${value}`));
26
+ vi.spyOn(fs, 'mkdirpSync').mockImplementation(() => undefined);
27
+ vi.spyOn(fs, 'copySync').mockImplementation((src, dest) =>
28
+ written.set(`${dest}`, written.get(`${src}`) ?? table.get(`${src}`) ?? ''),
29
+ );
30
+ vi.spyOn(fs, 'removeSync').mockImplementation((filePath) => {
31
+ written.delete(`${filePath}`);
32
+ removed.push(`${filePath}`);
33
+ });
34
+ return { written, removed };
35
+ };
36
+
37
+ const relative = (path) => `${path}`.replace(`${process.cwd()}/`, '');
38
+
39
+ describe('docker compose stack', () => {
40
+ let harness;
41
+
42
+ beforeEach(() => {
43
+ harness = shellHarness();
44
+ });
45
+
46
+ afterEach(() => {
47
+ harness.restore();
48
+ vi.restoreAllMocks();
49
+ });
50
+
51
+ describe('generated artifacts', () => {
52
+ it('renders the nginx routes, mongo entrypoint, monitoring config and env example', () => {
53
+ const { written } = composeFixture();
54
+ UnderpostDockerCompose.generate({});
55
+ const paths = [...written.keys()].map(relative);
56
+ expect(paths).to.include('docker/nginx/default.conf');
57
+ expect(paths).to.include('docker/mongodb/entrypoint.sh');
58
+ expect(paths).to.include('docker/prometheus/prometheus.yml');
59
+ expect(paths).to.include('docker/grafana/provisioning/datasources/datasource.yml');
60
+ expect(paths).to.include('docker/compose.env.example');
61
+ expect(paths).to.include('docker/compose.app.yml');
62
+ });
63
+
64
+ it('seeds the working env-file from the example only when it is absent', () => {
65
+ const { written } = composeFixture();
66
+ UnderpostDockerCompose.generate({});
67
+ const envPath = [...written.keys()].find((path) => relative(path) === 'docker/compose.env');
68
+ expect(written.get(envPath)).to.equal(written.get(`${envPath}.example`));
69
+
70
+ vi.restoreAllMocks();
71
+ const existing = composeFixture({ [envPath]: 'DB_PASSWORD=real\n' });
72
+ UnderpostDockerCompose.generate({});
73
+ expect(existing.written.has(envPath)).to.equal(false);
74
+ });
75
+
76
+ it('bakes the deploy id and environment into the app command override', () => {
77
+ const { written } = composeFixture();
78
+ UnderpostDockerCompose.generate({ deployId: 'dd-core', env: 'production' });
79
+ const override = written.get([...written.keys()].find((path) => relative(path) === 'docker/compose.app.yml'));
80
+ expect(override).to.include('dd-core');
81
+ expect(override).to.include('production');
82
+ });
83
+
84
+ it('self-bootstraps a fresh engine for the default deploy', () => {
85
+ expect(UnderpostDockerCompose.appCommand().join(' ')).to.include('underpost new engine');
86
+ expect(UnderpostDockerCompose.appCommand('dd-core', 'production').join(' ')).to.include(
87
+ 'underpost start --build --run dd-core production',
88
+ );
89
+ });
90
+
91
+ it('honours every generated path override', () => {
92
+ const { written } = composeFixture();
93
+ UnderpostDockerCompose.generate({
94
+ nginxConf: 'custom/nginx.conf',
95
+ envFile: 'custom/env',
96
+ appOverride: 'custom/app.yml',
97
+ });
98
+ const paths = [...written.keys()].map(relative);
99
+ expect(paths).to.include('custom/nginx.conf');
100
+ expect(paths).to.include('custom/env');
101
+ expect(paths).to.include('custom/app.yml');
102
+ });
103
+
104
+ it('scrapes the app service in the rendered prometheus config', () => {
105
+ expect(UnderpostDockerCompose.prometheusContent()).to.include('app:4001');
106
+ expect(UnderpostDockerCompose.grafanaDatasourceContent()).to.include('http://prometheus:9090');
107
+ expect(UnderpostDockerCompose.envExampleContent()).to.include('=');
108
+ expect(UnderpostDockerCompose.mongoEntrypointContent()).to.include('BOOTSTRAP_USER_CREATED');
109
+ });
110
+
111
+ // A named workflow is fully owned by its canonical directory: the generic
112
+ // CLI never writes application-specific content into it.
113
+ it('uses a custom workflow canonical files as-is', () => {
114
+ const base = 'engine-private/conf/dd-core/docker-compose/custom';
115
+ const { written } = composeFixture({
116
+ [UnderpostDockerCompose.resolve(`${base}/docker-compose.yml`)]: 'services: {}\n',
117
+ [UnderpostDockerCompose.resolve(`${base}/compose.env`)]: 'FIXTURE=true\n',
118
+ });
119
+ UnderpostDockerCompose.generate({ deployId: 'dd-core', dockerComposeId: 'custom' });
120
+ expect(written.size).to.equal(0);
121
+ });
122
+
123
+ it('names the canonical files a custom workflow is missing', () => {
124
+ composeFixture();
125
+ expect(() => UnderpostDockerCompose.generate({ deployId: 'dd-core', dockerComposeId: 'custom' })).to.throw(
126
+ 'missing',
127
+ );
128
+ });
129
+ });
130
+
131
+ describe('host installation', () => {
132
+ it('skips a platform the installer does not target', () => {
133
+ const previous = Object.getOwnPropertyDescriptor(process, 'platform');
134
+ Object.defineProperty(process, 'platform', { value: 'darwin', configurable: true });
135
+ try {
136
+ UnderpostDockerCompose.install({});
137
+ expect(harness.calls.length).to.equal(0);
138
+ } finally {
139
+ Object.defineProperty(process, 'platform', previous);
140
+ }
141
+ });
142
+
143
+ it('installs nothing when compose already works', () => {
144
+ composeFixture({ '/etc/redhat-release': 'Rocky Linux release 9\n' });
145
+ harness.route({ match: 'docker compose version', code: 0, stdout: 'v2.29.0\n' });
146
+ UnderpostDockerCompose.install({});
147
+ expect(harness.ran('dnf -y install docker-ce')).to.equal(false);
148
+ });
149
+
150
+ it('installs the engine and the compose plugin from the official repository', () => {
151
+ composeFixture({ '/etc/redhat-release': 'Rocky Linux release 9\n' });
152
+ harness.route({ match: 'docker compose version', code: 1, stdout: '' });
153
+ UnderpostDockerCompose.install({ user: 'operator' });
154
+ expect(harness.ran('download.docker.com/linux/rhel/docker-ce.repo')).to.equal(true);
155
+ expect(harness.ran('dnf -y install docker-ce docker-ce-cli containerd.io')).to.equal(true);
156
+ expect(harness.ran('systemctl enable --now docker')).to.equal(true);
157
+ expect(harness.ran('usermod -aG docker operator')).to.equal(true);
158
+ });
159
+
160
+ it('reinstalls under --force and warns on a host that is not RHEL-compatible', () => {
161
+ composeFixture();
162
+ harness.route({ match: 'docker compose version', code: 0, stdout: 'v2.29.0\n' });
163
+ UnderpostDockerCompose.install({ force: true, user: 'root' });
164
+ expect(harness.ran('dnf -y install docker-ce')).to.equal(true);
165
+ // root is already privileged; adding it to the group buys nothing.
166
+ expect(harness.ran('usermod -aG docker')).to.equal(false);
167
+ });
168
+ });
169
+
170
+ describe('reset', () => {
171
+ it('tears down containers, volumes and images, then prunes the generated artifacts', () => {
172
+ const { removed } = composeFixture({
173
+ [UnderpostDockerCompose.resolve('docker/nginx/default.conf')]: 'server {}\n',
174
+ [UnderpostDockerCompose.resolve('docker/compose.env')]: 'DB_PASSWORD=real\n',
175
+ });
176
+ UnderpostDockerCompose.reset({});
177
+ expect(harness.ran('down --remove-orphans --volumes --rmi local')).to.equal(true);
178
+ expect(removed.map(relative)).to.include('docker/nginx/default.conf');
179
+ // The working env-file holds credentials, so it survives a plain reset.
180
+ expect(removed.map(relative)).not.to.include('docker/compose.env');
181
+ });
182
+
183
+ it('drops the working env-file too under --force', () => {
184
+ const { removed } = composeFixture({ [UnderpostDockerCompose.resolve('docker/compose.env')]: 'DB=1\n' });
185
+ UnderpostDockerCompose.reset({ force: true });
186
+ expect(removed.map(relative)).to.include('docker/compose.env');
187
+ });
188
+
189
+ it('never prunes the files a custom workflow owns', () => {
190
+ const base = 'engine-private/conf/dd-core/docker-compose/custom';
191
+ const { removed } = composeFixture({
192
+ [UnderpostDockerCompose.resolve(`${base}/docker-compose.yml`)]: 'services: {}\n',
193
+ [UnderpostDockerCompose.resolve(`${base}/compose.env`)]: 'FIXTURE=true\n',
194
+ });
195
+ UnderpostDockerCompose.reset({ deployId: 'dd-core', dockerComposeId: 'custom' });
196
+ expect(harness.ran('down --remove-orphans')).to.equal(true);
197
+ expect(removed).to.deep.equal([]);
198
+ });
199
+ });
200
+
201
+ describe('CLI dispatch', () => {
202
+ const run = async (target, options) => {
203
+ composeFixture();
204
+ await UnderpostDockerCompose.API.callback(target, options);
205
+ };
206
+
207
+ it('brings the stack up when no action flag is given', async () => {
208
+ await run('', {});
209
+ expect(harness.ran('up -d')).to.equal(true);
210
+ });
211
+
212
+ it('rebuilds the images when asked to', async () => {
213
+ await run('', { up: true, build: true });
214
+ expect(harness.ran('up -d --build')).to.equal(true);
215
+ });
216
+
217
+ // baseCmd conditionally includes the app override, so it has to exist before
218
+ // the invocation is composed — notably after a reset prunes it.
219
+ it('regenerates the config before composing the invocation', async () => {
220
+ const { written } = composeFixture();
221
+ let generatedBeforeUp = false;
222
+ harness.route({
223
+ match: (command) => {
224
+ if (command.includes('up -d')) generatedBeforeUp = written.size > 0;
225
+ return false;
226
+ },
227
+ });
228
+ await UnderpostDockerCompose.API.callback('', { up: true });
229
+ expect(generatedBeforeUp).to.equal(true);
230
+ });
231
+
232
+ it('stops at the install when nothing else was requested', async () => {
233
+ composeFixture({ '/etc/redhat-release': 'Rocky\n' });
234
+ harness.route({ match: 'docker compose version', code: 0, stdout: 'v2\n' });
235
+ await UnderpostDockerCompose.API.callback('', { install: true });
236
+ expect(harness.ran('up -d')).to.equal(false);
237
+ });
238
+
239
+ it('continues past the install when a lifecycle flag follows it', async () => {
240
+ composeFixture({ '/etc/redhat-release': 'Rocky\n' });
241
+ harness.route({ match: 'docker compose version', code: 0, stdout: 'v2\n' });
242
+ await UnderpostDockerCompose.API.callback('', { install: true, up: true });
243
+ expect(harness.ran('up -d')).to.equal(true);
244
+ });
245
+
246
+ it('stops at the reset when nothing else was requested', async () => {
247
+ await run('', { reset: true });
248
+ expect(harness.ran('up -d')).to.equal(false);
249
+ });
250
+
251
+ it('recreates the stack when a reset is followed by an up', async () => {
252
+ await run('', { reset: true, up: true });
253
+ expect(harness.ran('up -d')).to.equal(true);
254
+ });
255
+
256
+ it('routes every remaining lifecycle flag to its compose subcommand', async () => {
257
+ for (const [options, expected] of [
258
+ [{ down: true }, 'down --remove-orphans'],
259
+ [{ down: true, volumes: true }, 'down --remove-orphans --volumes'],
260
+ [{ restart: true }, 'restart'],
261
+ [{ build: true }, 'build --no-cache'],
262
+ [{ pull: true }, 'pull'],
263
+ [{ logs: true }, 'logs -f --tail=200'],
264
+ [{ status: true }, 'ps --format'],
265
+ [{ exec: 'config' }, 'config'],
266
+ ]) {
267
+ harness.restore();
268
+ harness = shellHarness();
269
+ await run('', options);
270
+ expect(harness.ran(expected), JSON.stringify(options)).to.equal(true);
271
+ }
272
+ });
273
+
274
+ it('targets a single service where one is named', async () => {
275
+ await run('app', { logs: true });
276
+ expect(harness.ran('logs -f --tail=200 app')).to.equal(true);
277
+ harness.restore();
278
+ harness = shellHarness();
279
+ await run('mongodb', { restart: true });
280
+ expect(harness.ran('restart mongodb')).to.equal(true);
281
+ });
282
+
283
+ it('opens bash in the app service and sh in every other', async () => {
284
+ await run('', { shell: true });
285
+ expect(harness.ran('exec app /bin/bash')).to.equal(true);
286
+ harness.restore();
287
+ harness = shellHarness();
288
+ await run('mongodb', { shell: true });
289
+ expect(harness.ran('exec mongodb /bin/sh')).to.equal(true);
290
+ });
291
+
292
+ it('writes the config and runs nothing for a bare generate', async () => {
293
+ await run('', { generate: true });
294
+ expect(harness.calls.length).to.equal(0);
295
+ });
296
+
297
+ it('fails the process rather than half-applying a broken invocation', async () => {
298
+ const previousExit = process.exit;
299
+ let exitCode;
300
+ process.exit = (code) => {
301
+ exitCode = code;
302
+ };
303
+ try {
304
+ composeFixture();
305
+ harness.route({ match: 'up -d', throws: new Error('docker daemon unreachable') });
306
+ await UnderpostDockerCompose.API.callback('', { up: true });
307
+ expect(exitCode).to.equal(1);
308
+ } finally {
309
+ process.exit = previousExit;
310
+ }
311
+ });
312
+ });
313
+ });