@vaharoni/devops 1.0.47

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 (342) hide show
  1. package/README.md +51 -0
  2. package/dist/app-support/crypto/index.d.ts +15 -0
  3. package/dist/app-support/crypto/index.d.ts.map +1 -0
  4. package/dist/app-support/crypto/index.js +30 -0
  5. package/dist/app-support/crypto/internal-token.d.ts +20 -0
  6. package/dist/app-support/crypto/internal-token.d.ts.map +1 -0
  7. package/dist/app-support/crypto/internal-token.js +42 -0
  8. package/dist/app-support/crypto/internal-token.spec.d.ts +2 -0
  9. package/dist/app-support/crypto/internal-token.spec.d.ts.map +1 -0
  10. package/dist/app-support/crypto/internal-token.spec.js +45 -0
  11. package/dist/app-support/crypto/secret.d.ts +3 -0
  12. package/dist/app-support/crypto/secret.d.ts.map +1 -0
  13. package/dist/app-support/crypto/secret.js +12 -0
  14. package/dist/app-support/crypto/secret.spec.d.ts +2 -0
  15. package/dist/app-support/crypto/secret.spec.d.ts.map +1 -0
  16. package/dist/app-support/crypto/secret.spec.js +15 -0
  17. package/dist/app-support/discovery/dev-discovery-loader.d.ts +2 -0
  18. package/dist/app-support/discovery/dev-discovery-loader.d.ts.map +1 -0
  19. package/dist/app-support/discovery/dev-discovery-loader.js +30 -0
  20. package/dist/app-support/discovery/service-endpoint.d.ts +2 -0
  21. package/dist/app-support/discovery/service-endpoint.d.ts.map +1 -0
  22. package/dist/app-support/discovery/service-endpoint.js +10 -0
  23. package/dist/cli/affected.d.ts +11 -0
  24. package/dist/cli/affected.d.ts.map +1 -0
  25. package/dist/cli/affected.js +103 -0
  26. package/dist/cli/common.d.ts +89 -0
  27. package/dist/cli/common.d.ts.map +1 -0
  28. package/dist/cli/common.js +236 -0
  29. package/dist/cli/common.spec.d.ts +2 -0
  30. package/dist/cli/common.spec.d.ts.map +1 -0
  31. package/dist/cli/common.spec.js +64 -0
  32. package/dist/cli/console.d.ts +11 -0
  33. package/dist/cli/console.d.ts.map +1 -0
  34. package/dist/cli/console.js +35 -0
  35. package/dist/cli/constant.d.ts +11 -0
  36. package/dist/cli/constant.d.ts.map +1 -0
  37. package/dist/cli/constant.js +22 -0
  38. package/dist/cli/db.d.ts +11 -0
  39. package/dist/cli/db.d.ts.map +1 -0
  40. package/dist/cli/db.js +119 -0
  41. package/dist/cli/dml.d.ts +11 -0
  42. package/dist/cli/dml.d.ts.map +1 -0
  43. package/dist/cli/dml.js +116 -0
  44. package/dist/cli/env.d.ts +11 -0
  45. package/dist/cli/env.d.ts.map +1 -0
  46. package/dist/cli/env.js +67 -0
  47. package/dist/cli/exec.d.ts +11 -0
  48. package/dist/cli/exec.d.ts.map +1 -0
  49. package/dist/cli/exec.js +50 -0
  50. package/dist/cli/image.d.ts +11 -0
  51. package/dist/cli/image.d.ts.map +1 -0
  52. package/dist/cli/image.js +140 -0
  53. package/dist/cli/init.d.ts +11 -0
  54. package/dist/cli/init.d.ts.map +1 -0
  55. package/dist/cli/init.js +66 -0
  56. package/dist/cli/internal-curl.d.ts +11 -0
  57. package/dist/cli/internal-curl.d.ts.map +1 -0
  58. package/dist/cli/internal-curl.js +43 -0
  59. package/dist/cli/job.d.ts +11 -0
  60. package/dist/cli/job.d.ts.map +1 -0
  61. package/dist/cli/job.js +67 -0
  62. package/dist/cli/jwt.d.ts +11 -0
  63. package/dist/cli/jwt.d.ts.map +1 -0
  64. package/dist/cli/jwt.js +27 -0
  65. package/dist/cli/namespace.d.ts +11 -0
  66. package/dist/cli/namespace.d.ts.map +1 -0
  67. package/dist/cli/namespace.js +70 -0
  68. package/dist/cli/prep-build.d.ts +11 -0
  69. package/dist/cli/prep-build.d.ts.map +1 -0
  70. package/dist/cli/prep-build.js +82 -0
  71. package/dist/cli/prisma.d.ts +11 -0
  72. package/dist/cli/prisma.d.ts.map +1 -0
  73. package/dist/cli/prisma.js +25 -0
  74. package/dist/cli/redis.d.ts +11 -0
  75. package/dist/cli/redis.d.ts.map +1 -0
  76. package/dist/cli/redis.js +76 -0
  77. package/dist/cli/registry.d.ts +11 -0
  78. package/dist/cli/registry.d.ts.map +1 -0
  79. package/dist/cli/registry.js +58 -0
  80. package/dist/cli/run-many.d.ts +11 -0
  81. package/dist/cli/run-many.d.ts.map +1 -0
  82. package/dist/cli/run-many.js +50 -0
  83. package/dist/cli/run.d.ts +11 -0
  84. package/dist/cli/run.d.ts.map +1 -0
  85. package/dist/cli/run.js +37 -0
  86. package/dist/cli/template.d.ts +11 -0
  87. package/dist/cli/template.d.ts.map +1 -0
  88. package/dist/cli/template.js +123 -0
  89. package/dist/cli/test.d.ts +11 -0
  90. package/dist/cli/test.d.ts.map +1 -0
  91. package/dist/cli/test.js +28 -0
  92. package/dist/devops.d.ts +3 -0
  93. package/dist/devops.d.ts.map +1 -0
  94. package/dist/devops.js +103 -0
  95. package/dist/index.d.ts +4 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +3 -0
  98. package/dist/libs/affected-entities.d.ts +15 -0
  99. package/dist/libs/affected-entities.d.ts.map +1 -0
  100. package/dist/libs/affected-entities.js +52 -0
  101. package/dist/libs/config.d.ts +6 -0
  102. package/dist/libs/config.d.ts.map +1 -0
  103. package/dist/libs/config.js +98 -0
  104. package/dist/libs/dependencies.d.ts +19 -0
  105. package/dist/libs/dependencies.d.ts.map +1 -0
  106. package/dist/libs/dependencies.js +62 -0
  107. package/dist/libs/dependencies.spec.d.ts +2 -0
  108. package/dist/libs/dependencies.spec.d.ts.map +1 -0
  109. package/dist/libs/dependencies.spec.js +21 -0
  110. package/dist/libs/digital-ocean/container-reg.d.ts +6 -0
  111. package/dist/libs/digital-ocean/container-reg.d.ts.map +1 -0
  112. package/dist/libs/digital-ocean/container-reg.js +69 -0
  113. package/dist/libs/discovery/dependencies.d.ts +19 -0
  114. package/dist/libs/discovery/dependencies.d.ts.map +1 -0
  115. package/dist/libs/discovery/dependencies.js +62 -0
  116. package/dist/libs/discovery/dependencies.spec.d.ts +2 -0
  117. package/dist/libs/discovery/dependencies.spec.d.ts.map +1 -0
  118. package/dist/libs/discovery/dependencies.spec.js +21 -0
  119. package/dist/libs/discovery/images.d.ts +5 -0
  120. package/dist/libs/discovery/images.d.ts.map +1 -0
  121. package/dist/libs/discovery/images.js +45 -0
  122. package/dist/libs/discovery/index.d.ts +5 -0
  123. package/dist/libs/discovery/index.d.ts.map +1 -0
  124. package/dist/libs/discovery/index.js +55 -0
  125. package/dist/libs/discovery/package-json-processor.d.ts +3 -0
  126. package/dist/libs/discovery/package-json-processor.d.ts.map +1 -0
  127. package/dist/libs/discovery/package-json-processor.js +34 -0
  128. package/dist/libs/discovery/process-common.d.ts +25 -0
  129. package/dist/libs/discovery/process-common.d.ts.map +1 -0
  130. package/dist/libs/discovery/process-common.js +40 -0
  131. package/dist/libs/discovery/process-package-json.d.ts +3 -0
  132. package/dist/libs/discovery/process-package-json.d.ts.map +1 -0
  133. package/dist/libs/discovery/process-package-json.js +34 -0
  134. package/dist/libs/discovery/process-pyproject-toml.d.ts +3 -0
  135. package/dist/libs/discovery/process-pyproject-toml.d.ts.map +1 -0
  136. package/dist/libs/discovery/process-pyproject-toml.js +36 -0
  137. package/dist/libs/discovery/pyproject-toml-processor.d.ts +3 -0
  138. package/dist/libs/discovery/pyproject-toml-processor.d.ts.map +1 -0
  139. package/dist/libs/discovery/pyproject-toml-processor.js +39 -0
  140. package/dist/libs/git-helpers.d.ts +8 -0
  141. package/dist/libs/git-helpers.d.ts.map +1 -0
  142. package/dist/libs/git-helpers.js +20 -0
  143. package/dist/libs/hetzner/reg-secret.d.ts +3 -0
  144. package/dist/libs/hetzner/reg-secret.d.ts.map +1 -0
  145. package/dist/libs/hetzner/reg-secret.js +39 -0
  146. package/dist/libs/k8s-constants.d.ts +12 -0
  147. package/dist/libs/k8s-constants.d.ts.map +1 -0
  148. package/dist/libs/k8s-constants.js +66 -0
  149. package/dist/libs/k8s-db.d.ts +18 -0
  150. package/dist/libs/k8s-db.d.ts.map +1 -0
  151. package/dist/libs/k8s-db.js +73 -0
  152. package/dist/libs/k8s-generate.d.ts +17 -0
  153. package/dist/libs/k8s-generate.d.ts.map +1 -0
  154. package/dist/libs/k8s-generate.js +179 -0
  155. package/dist/libs/k8s-helpers.d.ts +11 -0
  156. package/dist/libs/k8s-helpers.d.ts.map +1 -0
  157. package/dist/libs/k8s-helpers.js +42 -0
  158. package/dist/libs/k8s-image-config.d.ts +8 -0
  159. package/dist/libs/k8s-image-config.d.ts.map +1 -0
  160. package/dist/libs/k8s-image-config.js +113 -0
  161. package/dist/libs/k8s-job-waiter.d.ts +8 -0
  162. package/dist/libs/k8s-job-waiter.d.ts.map +1 -0
  163. package/dist/libs/k8s-job-waiter.js +84 -0
  164. package/dist/libs/k8s-namespace.d.ts +7 -0
  165. package/dist/libs/k8s-namespace.d.ts.map +1 -0
  166. package/dist/libs/k8s-namespace.js +27 -0
  167. package/dist/libs/k8s-redis.d.ts +6 -0
  168. package/dist/libs/k8s-redis.d.ts.map +1 -0
  169. package/dist/libs/k8s-redis.js +31 -0
  170. package/dist/libs/k8s-secrets-manager.d.ts +5 -0
  171. package/dist/libs/k8s-secrets-manager.d.ts.map +1 -0
  172. package/dist/libs/k8s-secrets-manager.js +61 -0
  173. package/dist/libs/validate-env.d.ts +56 -0
  174. package/dist/libs/validate-env.d.ts.map +1 -0
  175. package/dist/libs/validate-env.js +214 -0
  176. package/dist/libs/validate-env.spec.d.ts +2 -0
  177. package/dist/libs/validate-env.spec.d.ts.map +1 -0
  178. package/dist/libs/validate-env.spec.js +168 -0
  179. package/dist/libs/workspace-discovery.d.ts +2 -0
  180. package/dist/libs/workspace-discovery.d.ts.map +1 -0
  181. package/dist/libs/workspace-discovery.js +75 -0
  182. package/dist/test.d.ts +2 -0
  183. package/dist/test.d.ts.map +1 -0
  184. package/dist/test.js +1 -0
  185. package/dist/types/index.d.ts +925 -0
  186. package/dist/types/index.d.ts.map +1 -0
  187. package/dist/types/index.js +79 -0
  188. package/package.json +55 -0
  189. package/src/app-support/crypto/index.ts +31 -0
  190. package/src/app-support/crypto/internal-token.spec.ts +53 -0
  191. package/src/app-support/crypto/internal-token.ts +82 -0
  192. package/src/app-support/crypto/secret.spec.ts +18 -0
  193. package/src/app-support/crypto/secret.ts +13 -0
  194. package/src/app-support/discovery/dev-discovery-loader.ts +35 -0
  195. package/src/app-support/discovery/service-endpoint.ts +12 -0
  196. package/src/cli/affected.ts +116 -0
  197. package/src/cli/common.spec.ts +78 -0
  198. package/src/cli/common.ts +323 -0
  199. package/src/cli/console.ts +46 -0
  200. package/src/cli/constant.ts +25 -0
  201. package/src/cli/db.ts +133 -0
  202. package/src/cli/dml.ts +126 -0
  203. package/src/cli/env.ts +87 -0
  204. package/src/cli/exec.sh +21 -0
  205. package/src/cli/exec.ts +57 -0
  206. package/src/cli/image.ts +197 -0
  207. package/src/cli/init.ts +75 -0
  208. package/src/cli/internal-curl.ts +48 -0
  209. package/src/cli/job.ts +80 -0
  210. package/src/cli/jwt.ts +32 -0
  211. package/src/cli/namespace.ts +78 -0
  212. package/src/cli/prep-build.ts +96 -0
  213. package/src/cli/prisma.ts +33 -0
  214. package/src/cli/redis.ts +83 -0
  215. package/src/cli/registry.ts +76 -0
  216. package/src/cli/run-many.ts +61 -0
  217. package/src/cli/run.ts +46 -0
  218. package/src/cli/template.ts +169 -0
  219. package/src/cli/test.ts +30 -0
  220. package/src/devops.ts +119 -0
  221. package/src/index.ts +3 -0
  222. package/src/libs/affected-entities.ts +71 -0
  223. package/src/libs/config.ts +117 -0
  224. package/src/libs/digital-ocean/container-reg.ts +81 -0
  225. package/src/libs/discovery/dependencies.spec.ts +25 -0
  226. package/src/libs/discovery/dependencies.ts +73 -0
  227. package/src/libs/discovery/images.ts +57 -0
  228. package/src/libs/discovery/index.ts +60 -0
  229. package/src/libs/discovery/process-common.ts +55 -0
  230. package/src/libs/discovery/process-package-json.ts +47 -0
  231. package/src/libs/discovery/process-pyproject-toml.ts +43 -0
  232. package/src/libs/git-helpers.ts +32 -0
  233. package/src/libs/hetzner/reg-secret.ts +54 -0
  234. package/src/libs/k8s-constants.ts +83 -0
  235. package/src/libs/k8s-db.ts +83 -0
  236. package/src/libs/k8s-generate.ts +211 -0
  237. package/src/libs/k8s-helpers.ts +59 -0
  238. package/src/libs/k8s-image-config.ts +165 -0
  239. package/src/libs/k8s-job-waiter.ts +124 -0
  240. package/src/libs/k8s-namespace.ts +41 -0
  241. package/src/libs/k8s-redis.ts +31 -0
  242. package/src/libs/k8s-secrets-manager.ts +79 -0
  243. package/src/libs/validate-env.spec.ts +223 -0
  244. package/src/libs/validate-env.ts +266 -0
  245. package/src/target-templates/.devops/config/constants.yaml +17 -0
  246. package/src/target-templates/.devops/config/images.yaml +88 -0
  247. package/src/target-templates/.devops/docker-images/common/docker-common.sh +23 -0
  248. package/src/target-templates/.devops/docker-images/node-services/node-exec.sh +8 -0
  249. package/src/target-templates/.devops/docker-images/node-services/node-run.sh +8 -0
  250. package/src/target-templates/.devops/docker-images/node-services.Dockerfile +34 -0
  251. package/src/target-templates/.devops/docker-images/python-services/python-exec.sh +8 -0
  252. package/src/target-templates/.devops/docker-images/python-services/python-run.sh +8 -0
  253. package/src/target-templates/.devops/docker-images/python-services.Dockerfile +29 -0
  254. package/src/target-templates/.devops/env.example.yaml +23 -0
  255. package/src/target-templates/.devops/infra/hetzner/abandoned/harbor-values.yaml +30 -0
  256. package/src/target-templates/.devops/infra/hetzner/abandoned/hcloud-config.yaml +134 -0
  257. package/src/target-templates/.devops/infra/hetzner/cert-manager.yaml +25 -0
  258. package/src/target-templates/.devops/infra/hetzner/harbor-cert.yaml +13 -0
  259. package/src/target-templates/.devops/infra/hetzner/harbor-values.yaml +76 -0
  260. package/src/target-templates/.devops/infra/hetzner/hcloud-config.yaml +113 -0
  261. package/src/target-templates/.devops/infra/hetzner/ingress-nginx-annotations.yaml +49 -0
  262. package/src/target-templates/.devops/infra/hetzner/ingress-nginx-configmap.yaml +8 -0
  263. package/src/target-templates/.devops/infra/hetzner/retain-storage-class.yaml +8 -0
  264. package/src/target-templates/.devops/infra/monitoring-ingress.yaml +62 -0
  265. package/src/target-templates/.devops/infra/stackgres-ui-ingress.yaml +35 -0
  266. package/src/target-templates/.devops/infra/test.yaml +60 -0
  267. package/src/target-templates/.devops/manifests/_index.yaml +21 -0
  268. package/src/target-templates/.devops/manifests/cron-jobs.yaml.hb +55 -0
  269. package/src/target-templates/.devops/manifests/db-migrate-job.yaml.hb +42 -0
  270. package/src/target-templates/.devops/manifests/deployment-debug.yaml.hb +44 -0
  271. package/src/target-templates/.devops/manifests/deployment-process.yaml.hb +47 -0
  272. package/src/target-templates/.devops/manifests/deployment-web.yaml.hb +53 -0
  273. package/src/target-templates/.devops/manifests/ingress.yaml.hb +21 -0
  274. package/src/target-templates/.devops/manifests/prefect.yaml.hb +62 -0
  275. package/src/target-templates/.devops/manifests/service.yaml.hb +15 -0
  276. package/src/target-templates/.devops/milvus/production/milvus-values.yaml +2 -0
  277. package/src/target-templates/.devops/milvus/staging/milvus-values.yaml +2 -0
  278. package/src/target-templates/.devops/postgres/DailyOperatorRestart.yaml +54 -0
  279. package/src/target-templates/.devops/postgres/production/cluster/PodDisruptionBudget.yaml +27 -0
  280. package/src/target-templates/.devops/postgres/production/cluster/SGCluster.yaml +47 -0
  281. package/src/target-templates/.devops/postgres/production/cluster/StackGres-alerts.yaml +191 -0
  282. package/src/target-templates/.devops/postgres/production/configurations/06-SGDistributedLogs.yaml +11 -0
  283. package/src/target-templates/.devops/postgres/production/configurations/07-SGObjectStorage.yaml +18 -0
  284. package/src/target-templates/.devops/postgres/production/configurations/08-SGScript.yaml +12 -0
  285. package/src/target-templates/.devops/postgres/staging/cluster/SGCluster.yaml +42 -0
  286. package/src/target-templates/.devops/postgres/staging/configurations/07-SGObjectStorage.yaml +18 -0
  287. package/src/target-templates/.devops/postgres/staging/configurations/08-SGScript.yaml +12 -0
  288. package/src/target-templates/.devops/prefect/production/prefect-values.yaml +14 -0
  289. package/src/target-templates/.devops/prefect/staging/prefect-values.yaml +14 -0
  290. package/src/target-templates/.devops/redis/production/redis-values.yaml +20 -0
  291. package/src/target-templates/.devops/redis/staging/redis-values.yaml +8 -0
  292. package/src/target-templates/.envrc +5 -0
  293. package/src/target-templates/.github/actions/build-image@v1/action.yaml +86 -0
  294. package/src/target-templates/.github/actions/connect-to-digital-ocean@v1/action.yaml +29 -0
  295. package/src/target-templates/.github/actions/connect-to-hetzner@v1/action.yaml +31 -0
  296. package/src/target-templates/.github/actions/connect-to-infra@v1/action.yaml +46 -0
  297. package/src/target-templates/.github/actions/db-migrate@v1/action.yaml +23 -0
  298. package/src/target-templates/.github/actions/deploy-image@v1/action.yaml +33 -0
  299. package/src/target-templates/.github/actions/setup-prereq@v1/action.yaml +29 -0
  300. package/src/target-templates/.github/workflows/k8s-build.yaml +84 -0
  301. package/src/target-templates/applications/example-data-pipeline/pyproject.toml +14 -0
  302. package/src/target-templates/applications/example-data-pipeline/src/example_data_pipeline/main.py +38 -0
  303. package/src/target-templates/applications/example-node/index.ts +30 -0
  304. package/src/target-templates/applications/example-node/package.json +26 -0
  305. package/src/target-templates/applications/example-node/tsconfig.json +3 -0
  306. package/src/target-templates/applications/example-python/pyproject.toml +20 -0
  307. package/src/target-templates/applications/example-python/src/example_python/__init__.py +0 -0
  308. package/src/target-templates/applications/example-python/src/example_python/main.py +13 -0
  309. package/src/target-templates/applications/example-python/src/example_python/scripts.py +17 -0
  310. package/src/target-templates/applications/example-python/tests/__init__.py +0 -0
  311. package/src/target-templates/applications/jobs/README.md +68 -0
  312. package/src/target-templates/applications/jobs/index.ts +1 -0
  313. package/src/target-templates/applications/jobs/package.json +30 -0
  314. package/src/target-templates/applications/jobs/tsconfig.json +3 -0
  315. package/src/target-templates/config/.env.development +1 -0
  316. package/src/target-templates/config/.env.global +4 -0
  317. package/src/target-templates/config/.env.test +1 -0
  318. package/src/target-templates/db/db/__init__.py +0 -0
  319. package/src/target-templates/db/db/db_client_test.py +46 -0
  320. package/src/target-templates/db/db-client-test.ts +140 -0
  321. package/src/target-templates/db/db-client.ts +19 -0
  322. package/src/target-templates/db/env.yaml +4 -0
  323. package/src/target-templates/db/package.json +17 -0
  324. package/src/target-templates/db/prisma/schema.prisma +24 -0
  325. package/src/target-templates/db/prisma-setup-vitest.ts +27 -0
  326. package/src/target-templates/db/pyproject.toml +14 -0
  327. package/src/target-templates/db/tsconfig.json +3 -0
  328. package/src/target-templates/devops +3 -0
  329. package/src/target-templates/devopspy +3 -0
  330. package/src/target-templates/dml/package.json +7 -0
  331. package/src/target-templates/dml/tsconfig.json +3 -0
  332. package/src/target-templates/libs/example-node-lib/bun.lock +27 -0
  333. package/src/target-templates/libs/example-node-lib/index.ts +3 -0
  334. package/src/target-templates/libs/example-node-lib/package.json +12 -0
  335. package/src/target-templates/libs/example-node-lib/tsconfig.json +3 -0
  336. package/src/target-templates/libs/example-python-lib/pyproject.toml +11 -0
  337. package/src/target-templates/libs/example-python-lib/src/example_python_lib/__init__.py +2 -0
  338. package/src/target-templates/pyproject.toml +19 -0
  339. package/src/target-templates/tmp/.gitkeep +0 -0
  340. package/src/target-templates/tsconfig.json +27 -0
  341. package/src/test.ts +0 -0
  342. package/src/types/index.ts +173 -0
@@ -0,0 +1,323 @@
1
+ import chalk from "chalk";
2
+ import { execSync, spawn } from "child_process";
3
+ import fs from "fs";
4
+ import { globSync } from "glob";
5
+ import { allSupportedEnvs } from "../libs/k8s-constants";
6
+
7
+ type ParsedArgs = {
8
+ args: string[];
9
+ argsStr: string;
10
+ options: { [key: string]: string | boolean };
11
+ passthrough?: string[];
12
+ };
13
+
14
+ interface ExecSyncError extends Error {
15
+ status?: number; // Exit code of the subprocess
16
+ stdout?: Buffer; // Standard output
17
+ stderr?: Buffer; // Standard error
18
+ }
19
+
20
+ export class CLICommandParser {
21
+ command: string;
22
+ args: string[];
23
+ env: string;
24
+ envForced: boolean;
25
+ help: boolean;
26
+ skipEnvCheck: boolean;
27
+
28
+ constructor(cmdArray: string[]) {
29
+ const parsedArgs = this._separateOptions(cmdArray.filter(Boolean), {
30
+ params: ["--env"],
31
+ booleans: ["--help", "--skip-env-check"],
32
+ });
33
+ const [command, ...args] = parsedArgs.args;
34
+ this.command = command;
35
+ this.args = args;
36
+ this.envForced = Boolean(parsedArgs.options["--env"]);
37
+ this.env =
38
+ (parsedArgs.options["--env"] as string) ??
39
+ process.env["MONOREPO_ENV"] ??
40
+ "development";
41
+ this.help = Boolean(parsedArgs.options["--help"]);
42
+ this.skipEnvCheck = Boolean(parsedArgs.options["--skip-env-check"]);
43
+ // We need to hardcode this to avoid chicken-and-egg problem (validate env depends on constants.yaml existence)
44
+ if (this.command !== 'init') {
45
+ this._validateEnv(this.env);
46
+ }
47
+ }
48
+
49
+ // Copies the env
50
+ executorFromEnv(
51
+ commandStr: string,
52
+ options: Omit<CommandExecutorOptions, "env"> = {}
53
+ ): CommandExecutor {
54
+ const checkEnvYamlOverride = this.skipEnvCheck
55
+ ? { checkEnvYaml: false }
56
+ : {};
57
+ return new CommandExecutor(commandStr, {
58
+ env: this.env,
59
+ ...options,
60
+ ...checkEnvYamlOverride,
61
+ });
62
+ }
63
+
64
+ // Example:
65
+ // const cmd = new CLICommandParser('devops run --some-flag --in workspace arg1'.split(' '));
66
+ // cmd.parseOptions({ boolean: ['--some-flag'], params: ['--in'] })
67
+ // # => { args: ['arg1'], options: { '--some-flag': true, '--in': 'workspace' } }
68
+ //
69
+ // Note that the global param --env is already extracted and can be accessed with cmd.env
70
+ parseOptions({
71
+ params = [],
72
+ booleans = [],
73
+ passthroughArgs = false,
74
+ }: {
75
+ /** Param is used like so: --param value */
76
+ params?: string[];
77
+ /** Boolean flag is used like so: --flag */
78
+ booleans?: string[];
79
+ /** Pass through args are used like so: -- arg1 arg2 */
80
+ passthroughArgs?: boolean;
81
+ } = {}): ParsedArgs {
82
+ return this._separateOptions(this.args, {
83
+ params,
84
+ booleans,
85
+ passthroughArgs,
86
+ });
87
+ }
88
+
89
+ _validateEnv(env: string) {
90
+ if (!allSupportedEnvs().includes(env)) {
91
+ console.error(
92
+ // prettier-ignore
93
+ `Environment must be one of: ${allSupportedEnvs().join(", ")}. Received: ${env}`
94
+ );
95
+ process.exit(1);
96
+ }
97
+ return true;
98
+ }
99
+
100
+ _separateOptions(
101
+ args: string[],
102
+ {
103
+ params = [],
104
+ booleans = [],
105
+ passthroughArgs = false,
106
+ }: {
107
+ params?: string[];
108
+ booleans?: string[];
109
+ passthroughArgs?: boolean;
110
+ } = {}
111
+ ): ParsedArgs {
112
+ const results: ParsedArgs = {
113
+ args: [],
114
+ argsStr: "",
115
+ options: {},
116
+ ...(passthroughArgs ? { passthrough: [] } : {}),
117
+ };
118
+ const paramsLookup = Object.fromEntries(params.map((x) => [x, true]));
119
+ const booleansLookup = Object.fromEntries(booleans.map((x) => [x, true]));
120
+ const passthroughArgsStart = passthroughArgs ? args.indexOf("--") : -1;
121
+ const numArgsToProcess =
122
+ passthroughArgsStart === -1 ? args.length : passthroughArgsStart;
123
+ for (let i = 0; i < numArgsToProcess; ++i) {
124
+ const curr = args[i];
125
+ if (paramsLookup[curr]) {
126
+ const next = args[i + 1];
127
+ results.options[curr] = next;
128
+ ++i;
129
+ } else if (booleansLookup[curr]) {
130
+ results.options[curr] = true;
131
+ } else {
132
+ results.args.push(curr);
133
+ }
134
+ }
135
+ results.argsStr = results.args.join(" ");
136
+ if (passthroughArgs && passthroughArgsStart >= 0) {
137
+ results.passthrough = args.slice(passthroughArgsStart + 1);
138
+ }
139
+ return results;
140
+ }
141
+ }
142
+
143
+ type CommandExecutorOptions = {
144
+ env?: string;
145
+ quiet?: boolean;
146
+ checkEnvYaml?: boolean;
147
+ redactedCommand?: string;
148
+ };
149
+ export class CommandExecutor {
150
+ commandStr: string;
151
+ env?: string;
152
+ quiet: boolean;
153
+ redactedCommand?: string;
154
+ checkEnvYaml: boolean;
155
+
156
+ constructor(
157
+ commandStr: string,
158
+ {
159
+ env,
160
+ quiet = false,
161
+ redactedCommand,
162
+ checkEnvYaml = false,
163
+ }: CommandExecutorOptions = {}
164
+ ) {
165
+ this.env = env;
166
+ this.quiet = quiet ?? false;
167
+ this.commandStr = commandStr;
168
+ this.checkEnvYaml = checkEnvYaml;
169
+ this.redactedCommand = redactedCommand;
170
+ }
171
+
172
+ /** Non-interactive use only. stdout is returned. */
173
+ exec(options?: {
174
+ onlyStatusCode?: false;
175
+ asObject?: false;
176
+ env?: object;
177
+ }): string;
178
+ exec(options: { onlyStatusCode?: false; asObject: true; env?: object }): {
179
+ statusCode: number;
180
+ stdout: string;
181
+ stderr: string;
182
+ };
183
+ exec(options: {
184
+ onlyStatusCode: true;
185
+ asObject?: boolean;
186
+ env?: object;
187
+ }): number;
188
+ exec({
189
+ onlyStatusCode = false,
190
+ asObject = false,
191
+ env = {},
192
+ }: { onlyStatusCode?: boolean; asObject?: boolean; env?: object } = {}) {
193
+ this._checkEnvYamlFiles();
194
+ const fullCommand = this._prepareFullCommand();
195
+ const envToUse = this._getProcessEnv(env);
196
+ try {
197
+ const output = execSync(fullCommand, { env: envToUse });
198
+ if (onlyStatusCode) return 0;
199
+ if (!this.quiet) console.log(output.toString());
200
+ if (asObject) return { statusCode: 0, stdout: output.toString() };
201
+ return output.toString();
202
+ } catch (error) {
203
+ const typedError = error as ExecSyncError;
204
+ if (onlyStatusCode) return typedError.status;
205
+ const stdout = typedError.stdout?.toString().trim();
206
+ const stderr = typedError.stderr?.toString().trim();
207
+ if (!this.quiet) {
208
+ console.warn(stdout);
209
+ console.error(stderr);
210
+ }
211
+ if (asObject) return { statusCode: typedError.status, stdout, stderr };
212
+ process.exit(typedError.status);
213
+ }
214
+ }
215
+
216
+ /** Should be used for CLI commands intended to be used locally. Provides interactivity. Unlike exec(), stdout is not returned. */
217
+ spawn({ env = {} } = {}) {
218
+ this._checkEnvYamlFiles();
219
+ const fullCommand = this._prepareFullCommand();
220
+ const envToUse = this._getProcessEnv(env);
221
+ return new Promise((resolve) => {
222
+ try {
223
+ const [cmd, ...args] = fullCommand.split(" ").filter(Boolean);
224
+ const childProcess = spawn(cmd, args, {
225
+ stdio: "inherit",
226
+ env: envToUse,
227
+ });
228
+
229
+ childProcess.on("close", (code) => {
230
+ if (code !== 0) {
231
+ console.error(chalk.red(`Process exited with code ${code}`));
232
+ process.exit(code);
233
+ }
234
+ resolve(code);
235
+ });
236
+
237
+ childProcess.on("error", (error) => {
238
+ console.error(chalk.red(`Error: ${error.message}`));
239
+ process.exit(1);
240
+ });
241
+ } catch (error) {
242
+ if (error instanceof Error) {
243
+ console.error(chalk.red(`${error.message}`));
244
+ }
245
+ process.exit(1);
246
+ }
247
+ });
248
+ }
249
+
250
+ _prepareFullCommand() {
251
+ const envPrefix = this.env ? this._envInjectorPrefix() : "";
252
+ const fullCommand = [envPrefix, this.commandStr].join(" ").trim();
253
+
254
+ const envPrefixLog = this.env ? `MONOREPO_ENV=${this.env}` : "";
255
+ const fullCommandLog = [envPrefixLog, fullCommand].join(" ").trim();
256
+ if (this.redactedCommand) {
257
+ console.warn(
258
+ chalk.yellow(
259
+ fullCommandLog.replace(this.commandStr, this.redactedCommand)
260
+ )
261
+ );
262
+ } else {
263
+ console.warn(chalk.yellow(fullCommandLog));
264
+ }
265
+
266
+ return fullCommand;
267
+ }
268
+
269
+ _getProcessEnv(envOverride = {}) {
270
+ return { ...process.env, MONOREPO_ENV: this.env, ...envOverride };
271
+ }
272
+
273
+ _envInjectorPrefix() {
274
+ const envFiles = dotEnvFilesForEnv(this.env);
275
+ if (envFiles.length === 0) {
276
+ return "";
277
+ } else {
278
+ return `bunx dotenvx -q run -f ${envFiles.join(" ")} -- `;
279
+ }
280
+ }
281
+
282
+ _checkEnvYamlFiles() {
283
+ if (!this.checkEnvYaml) return;
284
+ const envYamlFiles = globSync("**/env.yaml");
285
+ const checkEnvCmd = new CommandExecutor(
286
+ `devops env _validate ${envYamlFiles.join(" ")}`,
287
+ { env: this.env, quiet: true, checkEnvYaml: false }
288
+ );
289
+ checkEnvCmd.exec();
290
+ }
291
+ }
292
+
293
+ export function dotEnvFilesForEnv(env?: string) {
294
+ const globalEnvFilePath = "config/.env.global";
295
+ const specificEnvFilePath = `config/.env.${env}`;
296
+
297
+ const envFiles: string[] = [];
298
+ if (env && fs.existsSync(specificEnvFilePath))
299
+ envFiles.push(specificEnvFilePath);
300
+ if (fs.existsSync(globalEnvFilePath)) envFiles.push(globalEnvFilePath);
301
+ return envFiles;
302
+ }
303
+
304
+ export function printUsageAndExit(text: string): never {
305
+ console.warn(text);
306
+ process.exit(1);
307
+ }
308
+
309
+ export class StrongParams {
310
+ constructor(private usage: string, private args: Record<string, string | undefined>) {}
311
+
312
+ required(key: string) {
313
+ if (!this.args[key]) {
314
+ console.error(`Missing required argument: ${key}`);
315
+ printUsageAndExit(this.usage);
316
+ }
317
+ return this.args[key];
318
+ }
319
+
320
+ optional(key: string) {
321
+ return this.args[key];
322
+ }
323
+ }
@@ -0,0 +1,46 @@
1
+ import { getConst } from "../libs/config";
2
+ import {
3
+ envToNamespace,
4
+ imageDebugName,
5
+ } from "../libs/k8s-constants";
6
+ import { kubectlCommand } from "../libs/k8s-helpers";
7
+ import { CLICommandParser, printUsageAndExit } from "./common";
8
+
9
+ const oneLiner = "Get a shell into the debug pod of an image";
10
+ const keyExamples = `
11
+ $ devops console main-node
12
+ `.trim();
13
+
14
+ const usage = `
15
+ ${oneLiner}
16
+
17
+ Each image has a debug pod. This command gets a shell into the debug pod of the specified image.
18
+
19
+ EXAMPLES
20
+ ${keyExamples}
21
+ `;
22
+
23
+ function run(cmdObj: CLICommandParser) {
24
+ if (cmdObj.help || cmdObj.args.length === 0) printUsageAndExit(usage);
25
+ const image = cmdObj.args[0];
26
+ const debugName = imageDebugName(image);
27
+ const namespace = envToNamespace(cmdObj.env);
28
+ const podName = cmdObj
29
+ .executorFromEnv(
30
+ kubectlCommand(`get pod -n ${namespace} -l app=${debugName} -o name`, {
31
+ namespace,
32
+ }),
33
+ { quiet: true }
34
+ )
35
+ .exec()
36
+ .trim();
37
+ cmdObj
38
+ .executorFromEnv(
39
+ kubectlCommand(`exec -it ${podName} -- /bin/bash`, { namespace })
40
+ )
41
+ .spawn();
42
+ }
43
+
44
+ export default {
45
+ console: { oneLiner, keyExamples, run },
46
+ };
@@ -0,0 +1,25 @@
1
+ import { getConst } from "../libs/config";
2
+ import { CLICommandParser, printUsageAndExit } from "./common";
3
+
4
+ const oneLiner = "Prints to stdout a constant from constant.yaml";
5
+ const keyExamples = `$ devops constant infra`;
6
+
7
+ const usage = `
8
+ ${oneLiner}
9
+
10
+ GENERAL USAGE
11
+ devops constant <constant-name>
12
+
13
+ EXAMPLES
14
+ ${keyExamples}
15
+ `;
16
+
17
+ async function run(cmdObj: CLICommandParser) {
18
+ if (cmdObj.help || cmdObj.args.length === 0) printUsageAndExit(usage);
19
+ const [constant] = cmdObj.args;
20
+ console.log(getConst(constant as any));
21
+ }
22
+
23
+ export default {
24
+ constant: { oneLiner, keyExamples, run },
25
+ };
package/src/cli/db.ts ADDED
@@ -0,0 +1,133 @@
1
+ import {
2
+ connectToPatroni,
3
+ connectToPsql,
4
+ establishTunnel,
5
+ getDbAdminPassword,
6
+ getDbBackups,
7
+ getDbList,
8
+ getDbPasswords,
9
+ } from "../libs/k8s-db";
10
+ import { CLICommandParser, printUsageAndExit, StrongParams } from "./common";
11
+
12
+ const oneLiner =
13
+ "Utilities to help day to day operations of production and staging databases";
14
+ const keyExamples = `
15
+ $ devops db list
16
+ $ devops db backups
17
+ $ devops db password ui
18
+ $ devops db password db-staging
19
+ $ devops db tunnel db-staging
20
+ $ devops db patroni db-staging
21
+ $ devops db psql db-staging
22
+ `.trim();
23
+
24
+ const usage = `
25
+ ${oneLiner}
26
+
27
+ NOTES
28
+ The admin UI provided by Stackgres is great. It allows you to do most of the operations you need, such as
29
+ restarting the cluster, upgrading postgres versions, and restoring from backups with point in time recovery (PITR).
30
+
31
+ This utility complements the admin UI with a few helpful shortcuts.
32
+
33
+ Note that the --env flag should not be used with these commands, as the DB namespaces follow different
34
+ conventions than the monorepo env.
35
+
36
+ This utility assumes that the cluster name and the namespace are always the same.
37
+
38
+ COMMANDS
39
+ list Lists the available clusters
40
+ backups Lists all available backups
41
+ password ui Shows the password to the admin UI
42
+ password <namespace> Shows the superuser, replication, and authenticator password of the remote database
43
+ patroni <namespace> Obtain a shell to the primary pod's patroni container, where you can run 'patronictl'
44
+ psql <namespace> Runs 'psql' in the primary pod's postgres-utils container
45
+ tunnel <namespace> [-p <port>] Sets up a tunnel to the remote database so you can access the DB from your local machine.
46
+ By default, the port is taken from the namespace to make it easier to create connection profiles locally:
47
+ db-staging: 7432, db-production: 8432, otherwise: 9432
48
+
49
+ EXAMPLES
50
+ ${keyExamples}
51
+ `;
52
+
53
+ const DEFAULT_PORTS = {
54
+ "db-staging": "7432",
55
+ "db-production": "8432",
56
+ };
57
+
58
+ const handlers = {
59
+ list: () => {
60
+ const res = getDbList();
61
+ console.log(res);
62
+ },
63
+ backups: () => {
64
+ const res = getDbBackups();
65
+ console.log(res);
66
+ },
67
+ password: (opts: StrongParams) => {
68
+ const namespace = opts.required("namespace");
69
+ if (namespace === "ui") {
70
+ const res = getDbAdminPassword();
71
+ if (!res) {
72
+ console.error("Failed to get the secret");
73
+ process.exit(1);
74
+ } else {
75
+ console.log(`User: ${res.user}`);
76
+ console.log(`Password: ${res.password}`);
77
+ }
78
+ return;
79
+ }
80
+
81
+ const res = getDbPasswords(namespace);
82
+ if (!res) {
83
+ console.error("Failed to get the secret");
84
+ process.exit(1);
85
+ } else {
86
+ console.log("\nSuperuser");
87
+ console.log(` ${res.superUser}`);
88
+ console.log(` ${res.superPassword}`);
89
+ console.log("\nAuthenticator");
90
+ console.log(` ${res.authenticatorUser}`);
91
+ console.log(` ${res.authenticatorPassword}`);
92
+ console.log("\nReplication");
93
+ console.log(` ${res.replicationUser}`);
94
+ console.log(` ${res.replicationPassword}`);
95
+ console.log();
96
+ }
97
+ },
98
+ tunnel: (opts: StrongParams) => {
99
+ // prettier-ignore
100
+ const defaultPort = DEFAULT_PORTS[opts.required("namespace") as keyof typeof DEFAULT_PORTS] || "9432";
101
+ const port = opts.optional("port") || defaultPort;
102
+ establishTunnel(opts.required("namespace"), port);
103
+ },
104
+ patroni: (opts: StrongParams) => {
105
+ connectToPatroni(opts.required("namespace"));
106
+ },
107
+ psql: (opts: StrongParams) => {
108
+ connectToPsql(opts.required("namespace"));
109
+ },
110
+ };
111
+
112
+ function run(cmdObj: CLICommandParser) {
113
+ if (cmdObj.help || cmdObj.args.length < 1) printUsageAndExit(usage);
114
+ const parsed = cmdObj.parseOptions({ params: ["-p"] });
115
+
116
+ const [command, namespace] = parsed.args;
117
+ const port = parsed.options["-p"] as string;
118
+ // @ts-expect-error left as an exercise for the reader
119
+ const handler = handlers[command];
120
+ if (!handler) {
121
+ console.error(`Unknown command: ${command}`);
122
+ printUsageAndExit(usage);
123
+ }
124
+ const params = new StrongParams(usage, {
125
+ namespace,
126
+ port,
127
+ });
128
+ handler(params);
129
+ }
130
+
131
+ export default {
132
+ db: { oneLiner, keyExamples, run },
133
+ };
package/src/cli/dml.ts ADDED
@@ -0,0 +1,126 @@
1
+ import fs from "fs";
2
+ import { CLICommandParser, printUsageAndExit } from "./common";
3
+
4
+ const oneLiner = "Utilities to manage and run DML scripts in the db project";
5
+ const keyExamples = `
6
+ $ devops dml create --name my-dml-name
7
+ $ devops dml run 20250113153318_my_dml_name
8
+ `;
9
+
10
+ const usage = `
11
+ ${oneLiner}
12
+
13
+ CREATE DML SCRIPTS
14
+ devops dml create --name <dml-semantic-name>
15
+
16
+ This command creates a new folder under /dml using the current timestamp and a
17
+ snake-case version of the name. Inside the folder, a file called migrate.ts is created.
18
+ You should write your DML script in this file.
19
+
20
+ You can add additional artifacts to the folder, such as a README.md file, sql files, json
21
+ files, csv files, etc. You can also add optional scripts, such as rollback.ts.
22
+
23
+ RUN DML SCRIPTS
24
+ devops dml run <dml-folder-name> [script-file-name] [-- arg1 arg2 ...]
25
+
26
+ The dml-folder-name must be the full name, including the timestamp. This follows prisma
27
+ conventions.
28
+ If the optional script-file-name is omitted, 'migrate' is used by default. The name should
29
+ not include the '.ts' suffix.
30
+ Optionally, args can be passed to the script as command line arguments after double
31
+ dash (--).
32
+ The runner first changes the working directory to 'dml/', then executes the script using
33
+ 'bunx tsx'.
34
+
35
+ Note: DML scripts are typically run inside the debug container of the image.
36
+
37
+ EXAMPLES
38
+ ${keyExamples.trim()}
39
+ $ devops dml run 20250113153318_my_dml_name rollback
40
+ $ devops dml run 20250113153318_my_dml_name -- staging
41
+ `;
42
+
43
+ const dmlFileTemplate = `
44
+ /**
45
+ * Header code that retrieves the context of the DML script.
46
+ * Feel free to modify this code to suit your needs.
47
+ *
48
+ * fullDmlFilePath - path to the current file
49
+ * fullDmlDirPath - path to the current directory
50
+ * dmlFile - name of the current DML script file
51
+ * dmlDir - name of the directory containing the DML scripts
52
+ * args - command line arguments passed to the script
53
+ *
54
+ * Notes:
55
+ * - the script runs with the cwd set to the dml/ directory
56
+ * - remove unused variables from this template, otherwise the linter will complain
57
+ */
58
+
59
+ import { prisma } from 'db';
60
+ import { basename, dirname, sep } from 'path';
61
+ import { fileURLToPath } from 'url';
62
+
63
+ const fullDmlFilePath = fileURLToPath(import.meta.url);
64
+ const fullDmlDirPath = dirname(fullDmlFilePath);
65
+ const dmlFile = basename(fullDmlFilePath);
66
+ const dmlDir = fullDmlDirPath.split(sep).pop();
67
+
68
+ const args = process.argv.slice(2);
69
+ `.trim();
70
+
71
+ function createDml(name: string) {
72
+ const timestamp = new Date()
73
+ .toISOString()
74
+ .replace(/[^0-9]/g, "")
75
+ .slice(0, 14);
76
+ const nameSnake = name.toLowerCase().replace(/[^a-z0-9]/g, "_");
77
+ const folderName = `${timestamp}_${nameSnake}`;
78
+ fs.mkdirSync(`dml/${folderName}`);
79
+ fs.writeFileSync(`dml/${folderName}/migrate.ts`, dmlFileTemplate);
80
+ console.log(`\nCreated DML folder: dml/${folderName}\n`);
81
+ }
82
+
83
+ function runDml(
84
+ cmdObj: CLICommandParser,
85
+ folderName: string,
86
+ scriptFileName?: string,
87
+ args?: string[]
88
+ ) {
89
+ scriptFileName ??= "migrate";
90
+ args ??= [];
91
+ const script = scriptFileName.endsWith(".ts")
92
+ ? scriptFileName
93
+ : `${scriptFileName}.ts`;
94
+ cmdObj
95
+ .executorFromEnv(
96
+ // prettier-ignore
97
+ `devops exec --in dml bun ${folderName}/${script} ${args.join(" ")}`
98
+ )
99
+ .exec();
100
+ }
101
+
102
+ function run(cmdObj: CLICommandParser) {
103
+ if (cmdObj.help || cmdObj.args.length < 1) printUsageAndExit(usage);
104
+ const parsed = cmdObj.parseOptions({
105
+ passthroughArgs: true,
106
+ params: ["--name"],
107
+ });
108
+ switch (parsed.args[0]) {
109
+ case "create": {
110
+ const name = parsed.options["--name"] as string;
111
+ if (!name) printUsageAndExit(usage);
112
+ return createDml(name);
113
+ }
114
+ case "run": {
115
+ const [_, folderName, scriptFileName] = parsed.args;
116
+ if (!folderName) printUsageAndExit(usage);
117
+ return runDml(cmdObj, folderName, scriptFileName, parsed.passthrough);
118
+ }
119
+ default:
120
+ printUsageAndExit(usage);
121
+ }
122
+ }
123
+
124
+ export default {
125
+ dml: { oneLiner, keyExamples, run },
126
+ };