@geekmidas/cli 10.0.0-alpha.65 → 10.0.0-alpha.67

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 (229) hide show
  1. package/README.md +123 -177
  2. package/dist/{HostingerProvider-x9gQTAGZ.mjs → HostingerProvider-D85ayzTi.mjs} +4 -4
  3. package/dist/HostingerProvider-D85ayzTi.mjs.map +1 -0
  4. package/dist/{HostingerProvider-Dlmbfass.cjs → HostingerProvider-dxkwynSs.cjs} +4 -4
  5. package/dist/HostingerProvider-dxkwynSs.cjs.map +1 -0
  6. package/dist/{LegacyStateStore-BGoo__sO.cjs → LegacyStateStore-BZiO9-Lw.cjs} +2 -2
  7. package/dist/{LegacyStateStore-BGoo__sO.cjs.map → LegacyStateStore-BZiO9-Lw.cjs.map} +1 -1
  8. package/dist/{LegacyStateStore-BUu1nQ5u.mjs → LegacyStateStore-C39UKwmf.mjs} +2 -2
  9. package/dist/{LegacyStateStore-BUu1nQ5u.mjs.map → LegacyStateStore-C39UKwmf.mjs.map} +1 -1
  10. package/dist/{LocalStateStore-93urXyQe.cjs → LocalStateStore-DHVsZLyF.cjs} +2 -2
  11. package/dist/{LocalStateStore-93urXyQe.cjs.map → LocalStateStore-DHVsZLyF.cjs.map} +1 -1
  12. package/dist/{LocalStateStore-BRhMQRuy.mjs → LocalStateStore-Dk3Wbvzk.mjs} +2 -2
  13. package/dist/{LocalStateStore-BRhMQRuy.mjs.map → LocalStateStore-Dk3Wbvzk.mjs.map} +1 -1
  14. package/dist/{OpenApiTsGenerator-BWuDRHe9.cjs → OpenApiTsGenerator-B2zrdyP4.cjs} +2 -2
  15. package/dist/{OpenApiTsGenerator-BWuDRHe9.cjs.map → OpenApiTsGenerator-B2zrdyP4.cjs.map} +1 -1
  16. package/dist/{OpenApiTsGenerator-C6v-IeHt.mjs → OpenApiTsGenerator-BmUAcR7d.mjs} +2 -2
  17. package/dist/{OpenApiTsGenerator-C6v-IeHt.mjs.map → OpenApiTsGenerator-BmUAcR7d.mjs.map} +1 -1
  18. package/dist/{Route53Provider-DVILBsvV.cjs → Route53Provider-Bllus2NR.cjs} +2 -2
  19. package/dist/Route53Provider-Bllus2NR.cjs.map +1 -0
  20. package/dist/{Route53Provider-Cvj40A8I.mjs → Route53Provider-QXxjkZO1.mjs} +2 -2
  21. package/dist/Route53Provider-QXxjkZO1.mjs.map +1 -0
  22. package/dist/{S3StateStore-F4dxw6kS.cjs → S3StateStore--4tYVMAL.cjs} +2 -2
  23. package/dist/{S3StateStore-F4dxw6kS.cjs.map → S3StateStore--4tYVMAL.cjs.map} +1 -1
  24. package/dist/{S3StateStore-pw1GhWwt.mjs → S3StateStore-B_ZF9eHt.mjs} +2 -2
  25. package/dist/{S3StateStore-pw1GhWwt.mjs.map → S3StateStore-B_ZF9eHt.mjs.map} +1 -1
  26. package/dist/{SSMStateStore-Bt9acuAF.cjs → SSMStateStore-ACZ8hRvZ.cjs} +2 -2
  27. package/dist/{SSMStateStore-Bt9acuAF.cjs.map → SSMStateStore-ACZ8hRvZ.cjs.map} +1 -1
  28. package/dist/{SSMStateStore-Dmj7zzFO.mjs → SSMStateStore-DjPuKqrm.mjs} +2 -2
  29. package/dist/{SSMStateStore-Dmj7zzFO.mjs.map → SSMStateStore-DjPuKqrm.mjs.map} +1 -1
  30. package/dist/{backup-provisioner-DIVCbfee.mjs → backup-provisioner-C8DnCmxK.mjs} +2 -2
  31. package/dist/backup-provisioner-C8DnCmxK.mjs.map +1 -0
  32. package/dist/{backup-provisioner-BmhIO_Yr.cjs → backup-provisioner-oDEsPj02.cjs} +2 -2
  33. package/dist/backup-provisioner-oDEsPj02.cjs.map +1 -0
  34. package/dist/{bundler-D_14JLZF.mjs → bundler-Bq3HaLOZ.mjs} +20 -5
  35. package/dist/bundler-Bq3HaLOZ.mjs.map +1 -0
  36. package/dist/{bundler-D6MlIfBT.cjs → bundler-pyLj-gS0.cjs} +20 -5
  37. package/dist/bundler-pyLj-gS0.cjs.map +1 -0
  38. package/dist/{cli-P56vvrDQ.mjs → cli-C2Tjbn27.mjs} +6 -5
  39. package/dist/cli-C2Tjbn27.mjs.map +1 -0
  40. package/dist/{cli-BfH1deAT.cjs → cli-DR7B2_fX.cjs} +6 -5
  41. package/dist/cli-DR7B2_fX.cjs.map +1 -0
  42. package/dist/{compose-391oEKj1.cjs → compose-5oNHAKNT.cjs} +137 -667
  43. package/dist/compose-5oNHAKNT.cjs.map +1 -0
  44. package/dist/{compose-DprE3ASm.mjs → compose-BjToENzG.mjs} +130 -569
  45. package/dist/compose-BjToENzG.mjs.map +1 -0
  46. package/dist/{config-V_X_BMpm.d.cts → config-4XFxX1al.d.cts} +3 -3
  47. package/dist/{config-BonvTW8p.d.mts.map → config-4XFxX1al.d.cts.map} +1 -1
  48. package/dist/{config-BonvTW8p.d.mts → config-UtKzhpFb.d.mts} +3 -3
  49. package/dist/{config-V_X_BMpm.d.cts.map → config-UtKzhpFb.d.mts.map} +1 -1
  50. package/dist/config.cjs +3 -3
  51. package/dist/config.d.cts +3 -3
  52. package/dist/config.d.mts +3 -3
  53. package/dist/config.mjs +3 -3
  54. package/dist/{credentials-Bzbg23n8.cjs → credentials-BVs7mrtw.cjs} +239 -3
  55. package/dist/credentials-BVs7mrtw.cjs.map +1 -0
  56. package/dist/{credentials-Dq1MnWQf.mjs → credentials-pAtB70k3.mjs} +223 -5
  57. package/dist/credentials-pAtB70k3.mjs.map +1 -0
  58. package/dist/databases-C2p5LGAp.cjs +419 -0
  59. package/dist/databases-C2p5LGAp.cjs.map +1 -0
  60. package/dist/databases-hUHTuwhx.mjs +346 -0
  61. package/dist/databases-hUHTuwhx.mjs.map +1 -0
  62. package/dist/deploy/public.cjs +37 -28
  63. package/dist/deploy/public.cjs.map +1 -1
  64. package/dist/deploy/public.d.cts +136 -19
  65. package/dist/deploy/public.d.cts.map +1 -1
  66. package/dist/deploy/public.d.mts +136 -19
  67. package/dist/deploy/public.d.mts.map +1 -1
  68. package/dist/deploy/public.mjs +11 -11
  69. package/dist/deploy-BucRfbvX.cjs +511 -0
  70. package/dist/deploy-BucRfbvX.cjs.map +1 -0
  71. package/dist/deploy-DRAwTG72.mjs +488 -0
  72. package/dist/deploy-DRAwTG72.mjs.map +1 -0
  73. package/dist/{discover-LMrqsWSU.mjs → discover-BFoJWKGD.mjs} +4 -2
  74. package/dist/discover-BFoJWKGD.mjs.map +1 -0
  75. package/dist/{discover-DHdECueV.cjs → discover-OEfZ9OkA.cjs} +4 -2
  76. package/dist/discover-OEfZ9OkA.cjs.map +1 -0
  77. package/dist/dokploy-DIt1IZl1.mjs +38 -0
  78. package/dist/dokploy-DIt1IZl1.mjs.map +1 -0
  79. package/dist/dokploy-m345Zx02.cjs +38 -0
  80. package/dist/dokploy-m345Zx02.cjs.map +1 -0
  81. package/dist/{encryption-DF_hgIpl.mjs → encryption-Ccbahik7.mjs} +3 -5
  82. package/dist/{encryption-DF_hgIpl.mjs.map → encryption-Ccbahik7.mjs.map} +1 -1
  83. package/dist/{encryption-B1wJwgae.cjs → encryption-rZ3BghXy.cjs} +5 -7
  84. package/dist/{encryption-B1wJwgae.cjs.map → encryption-rZ3BghXy.cjs.map} +1 -1
  85. package/dist/{deploy-Cy8igpQF.cjs → engine-Cx_dvx9q.cjs} +1491 -1260
  86. package/dist/engine-Cx_dvx9q.cjs.map +1 -0
  87. package/dist/{deploy-CZ5nlpsu.mjs → engine-Gs0abjuj.mjs} +1450 -1261
  88. package/dist/engine-Gs0abjuj.mjs.map +1 -0
  89. package/dist/{github-B0YYE98x.mjs → github-CJANTYqi.mjs} +3 -3
  90. package/dist/{github-B0YYE98x.mjs.map → github-CJANTYqi.mjs.map} +1 -1
  91. package/dist/{github-CYcmTdFs.cjs → github-CrcGFkfD.cjs} +7 -7
  92. package/dist/{github-CYcmTdFs.cjs.map → github-CrcGFkfD.cjs.map} +1 -1
  93. package/dist/{index-DPUAGJaK.d.cts → index-CyApaHVe.d.cts} +2 -2
  94. package/dist/{index-DPUAGJaK.d.cts.map → index-CyApaHVe.d.cts.map} +1 -1
  95. package/dist/{index-DwKD_8lt.d.mts → index-DD1FfUHh.d.mts} +2 -2
  96. package/dist/{index-DwKD_8lt.d.mts.map → index-DD1FfUHh.d.mts.map} +1 -1
  97. package/dist/index.cjs +285 -91
  98. package/dist/index.cjs.map +1 -1
  99. package/dist/index.mjs +258 -68
  100. package/dist/index.mjs.map +1 -1
  101. package/dist/{migrate-q4pPAXtO.cjs → migrate-CQdSpttI.cjs} +16 -16
  102. package/dist/{migrate-q4pPAXtO.cjs.map → migrate-CQdSpttI.cjs.map} +1 -1
  103. package/dist/{migrate-B2qL5wj4.mjs → migrate-DCVo5gLS.mjs} +6 -6
  104. package/dist/{migrate-B2qL5wj4.mjs.map → migrate-DCVo5gLS.mjs.map} +1 -1
  105. package/dist/openapi.cjs +1 -1
  106. package/dist/openapi.d.cts +1 -1
  107. package/dist/openapi.d.mts +1 -1
  108. package/dist/openapi.mjs +1 -1
  109. package/dist/provider-B3Sv_a74.cjs +173 -0
  110. package/dist/provider-B3Sv_a74.cjs.map +1 -0
  111. package/dist/provider-D7rQzzzf.mjs +138 -0
  112. package/dist/provider-D7rQzzzf.mjs.map +1 -0
  113. package/dist/{ready-bQfu9GES.mjs → ready-2s3-CndS.mjs} +3 -3
  114. package/dist/{ready-bQfu9GES.mjs.map → ready-2s3-CndS.mjs.map} +1 -1
  115. package/dist/{ready-brEhh68f.cjs → ready-B6uHr4sm.cjs} +3 -3
  116. package/dist/{ready-brEhh68f.cjs.map → ready-B6uHr4sm.cjs.map} +1 -1
  117. package/dist/reconcile/public.cjs +1 -1
  118. package/dist/reconcile/public.d.cts +1 -1
  119. package/dist/reconcile/public.d.mts +1 -1
  120. package/dist/reconcile/public.mjs +1 -1
  121. package/dist/{resolve-N_i4XNd-.cjs → resolve-BexmDcsN.cjs} +4 -4
  122. package/dist/{resolve-N_i4XNd-.cjs.map → resolve-BexmDcsN.cjs.map} +1 -1
  123. package/dist/{resolve-DLkIXTct.mjs → resolve-DhZgtvZe.mjs} +4 -4
  124. package/dist/{resolve-DLkIXTct.mjs.map → resolve-DhZgtvZe.mjs.map} +1 -1
  125. package/dist/rollback-BnY7KuG_.cjs +161 -0
  126. package/dist/rollback-BnY7KuG_.cjs.map +1 -0
  127. package/dist/rollback-m4Yeym5x.mjs +132 -0
  128. package/dist/rollback-m4Yeym5x.mjs.map +1 -0
  129. package/dist/run-BJLIerMM.mjs +133 -0
  130. package/dist/run-BJLIerMM.mjs.map +1 -0
  131. package/dist/run-fVZ-rLnc.cjs +174 -0
  132. package/dist/run-fVZ-rLnc.cjs.map +1 -0
  133. package/dist/{runtime-BnD7Nn96.d.cts → runtime-BRqFOvPM.d.cts} +2 -2
  134. package/dist/{runtime-BnD7Nn96.d.cts.map → runtime-BRqFOvPM.d.cts.map} +1 -1
  135. package/dist/{runtime-j-tmFL65.d.mts → runtime-CEk5n4VH.d.mts} +2 -2
  136. package/dist/{runtime-j-tmFL65.d.mts.map → runtime-CEk5n4VH.d.mts.map} +1 -1
  137. package/dist/sandbox/discover-worker.cjs +1 -1
  138. package/dist/sandbox/discover-worker.mjs +1 -1
  139. package/dist/sandbox/migrate-worker.cjs +61 -0
  140. package/dist/sandbox/migrate-worker.cjs.map +1 -0
  141. package/dist/sandbox/migrate-worker.d.cts +1 -0
  142. package/dist/sandbox/migrate-worker.d.mts +1 -0
  143. package/dist/sandbox/migrate-worker.mjs +62 -0
  144. package/dist/sandbox/migrate-worker.mjs.map +1 -0
  145. package/dist/{schemas-D2LCF689.mjs → schemas-Cfhtpdo0.mjs} +10 -2
  146. package/dist/{schemas-Ckm_aBnF.cjs.map → schemas-Cfhtpdo0.mjs.map} +1 -1
  147. package/dist/{schemas-Ckm_aBnF.cjs → schemas-RUlKHu27.cjs} +15 -1
  148. package/dist/{schemas-D2LCF689.mjs.map → schemas-RUlKHu27.cjs.map} +1 -1
  149. package/dist/{sniffer-BZ-OJO4Y.mjs → sniffer-BdFeLSmA.mjs} +97 -600
  150. package/dist/sniffer-BdFeLSmA.mjs.map +1 -0
  151. package/dist/{sniffer-BSwaL-dX.cjs → sniffer-D66HIvi9.cjs} +110 -674
  152. package/dist/sniffer-D66HIvi9.cjs.map +1 -0
  153. package/dist/{sst-B2HbrDR3.mjs → sst-DRi3H5TB.mjs} +3 -3
  154. package/dist/{sst-B2HbrDR3.mjs.map → sst-DRi3H5TB.mjs.map} +1 -1
  155. package/dist/{sst-DcfsVH6x.cjs → sst-hBrKouBy.cjs} +5 -5
  156. package/dist/{sst-DcfsVH6x.cjs.map → sst-hBrKouBy.cjs.map} +1 -1
  157. package/dist/{state-DF26e_lo.cjs → state-DgmM4EKq.cjs} +2787 -1890
  158. package/dist/state-DgmM4EKq.cjs.map +1 -0
  159. package/dist/{state-B8YwmUZU.mjs → state-DkUStoqJ.mjs} +2813 -2084
  160. package/dist/state-DkUStoqJ.mjs.map +1 -0
  161. package/dist/target/public.cjs +1 -1
  162. package/dist/target/public.d.cts +2 -2
  163. package/dist/target/public.d.mts +2 -2
  164. package/dist/target/public.mjs +1 -1
  165. package/dist/{terminal-CfbiYUbZ.mjs → terminal-C3cv-OQu.mjs} +5 -5
  166. package/dist/{terminal-CfbiYUbZ.mjs.map → terminal-C3cv-OQu.mjs.map} +1 -1
  167. package/dist/{terminal-Bs5E1oxm.cjs → terminal-DjzgLu6m.cjs} +12 -16
  168. package/dist/{terminal-Bs5E1oxm.cjs.map → terminal-DjzgLu6m.cjs.map} +1 -1
  169. package/dist/{types-ByMWkXKP.d.cts → types-CSw2w69a.d.mts} +68 -10
  170. package/dist/types-CSw2w69a.d.mts.map +1 -0
  171. package/dist/{types-BCzMWBb6.d.mts → types-D5WIxRDP.d.cts} +69 -9
  172. package/dist/types-D5WIxRDP.d.cts.map +1 -0
  173. package/dist/vitest.cjs +1 -1
  174. package/dist/vitest.mjs +1 -1
  175. package/dist/workspace/index.cjs +1 -1
  176. package/dist/workspace/index.d.cts +2 -2
  177. package/dist/workspace/index.d.mts +2 -2
  178. package/dist/workspace/index.mjs +1 -1
  179. package/dist/{workspace-C06_pW4g.mjs → workspace-Be6uBhlF.mjs} +10 -3
  180. package/dist/workspace-Be6uBhlF.mjs.map +1 -0
  181. package/dist/{workspace-BewAHZIo.cjs → workspace-ON-bqiJz.cjs} +10 -3
  182. package/dist/workspace-ON-bqiJz.cjs.map +1 -0
  183. package/package.json +13 -13
  184. package/dist/HostingerProvider-Dlmbfass.cjs.map +0 -1
  185. package/dist/HostingerProvider-x9gQTAGZ.mjs.map +0 -1
  186. package/dist/Route53Provider-Cvj40A8I.mjs.map +0 -1
  187. package/dist/Route53Provider-DVILBsvV.cjs.map +0 -1
  188. package/dist/backup-provisioner-BmhIO_Yr.cjs.map +0 -1
  189. package/dist/backup-provisioner-DIVCbfee.mjs.map +0 -1
  190. package/dist/bundler-D6MlIfBT.cjs.map +0 -1
  191. package/dist/bundler-D_14JLZF.mjs.map +0 -1
  192. package/dist/cli-BfH1deAT.cjs.map +0 -1
  193. package/dist/cli-P56vvrDQ.mjs.map +0 -1
  194. package/dist/compose-391oEKj1.cjs.map +0 -1
  195. package/dist/compose-DprE3ASm.mjs.map +0 -1
  196. package/dist/credentials-Bzbg23n8.cjs.map +0 -1
  197. package/dist/credentials-Dq1MnWQf.mjs.map +0 -1
  198. package/dist/deploy-9MBG_QLd.mjs +0 -137
  199. package/dist/deploy-9MBG_QLd.mjs.map +0 -1
  200. package/dist/deploy-B80idPUZ.cjs +0 -142
  201. package/dist/deploy-B80idPUZ.cjs.map +0 -1
  202. package/dist/deploy-CZ5nlpsu.mjs.map +0 -1
  203. package/dist/deploy-Cy8igpQF.cjs.map +0 -1
  204. package/dist/discover-DHdECueV.cjs.map +0 -1
  205. package/dist/discover-LMrqsWSU.mjs.map +0 -1
  206. package/dist/dokploy-BdtBePjp.mjs +0 -32
  207. package/dist/dokploy-BdtBePjp.mjs.map +0 -1
  208. package/dist/dokploy-iFY8MY-L.cjs +0 -32
  209. package/dist/dokploy-iFY8MY-L.cjs.map +0 -1
  210. package/dist/local-BdH4XN0E.cjs +0 -409
  211. package/dist/local-BdH4XN0E.cjs.map +0 -1
  212. package/dist/local-CWR4Y_kW.mjs +0 -350
  213. package/dist/local-CWR4Y_kW.mjs.map +0 -1
  214. package/dist/sniffer-BSwaL-dX.cjs.map +0 -1
  215. package/dist/sniffer-BZ-OJO4Y.mjs.map +0 -1
  216. package/dist/state-B8YwmUZU.mjs.map +0 -1
  217. package/dist/state-DF26e_lo.cjs.map +0 -1
  218. package/dist/storage-BzaUxmqe.mjs +0 -124
  219. package/dist/storage-BzaUxmqe.mjs.map +0 -1
  220. package/dist/storage-CSGoC3bK.cjs +0 -152
  221. package/dist/storage-CSGoC3bK.cjs.map +0 -1
  222. package/dist/store-CJF1Tqkq.cjs +0 -510
  223. package/dist/store-CJF1Tqkq.cjs.map +0 -1
  224. package/dist/store-CLiHnqOY.mjs +0 -415
  225. package/dist/store-CLiHnqOY.mjs.map +0 -1
  226. package/dist/types-BCzMWBb6.d.mts.map +0 -1
  227. package/dist/types-ByMWkXKP.d.cts.map +0 -1
  228. package/dist/workspace-BewAHZIo.cjs.map +0 -1
  229. package/dist/workspace-C06_pW4g.mjs.map +0 -1
@@ -1,21 +1,15 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
- import { c as runOutput, s as run, t as LocalSandbox } from "./local-CWR4Y_kW.mjs";
3
- import { r as discover } from "./discover-LMrqsWSU.mjs";
4
- import { i as activeSandbox } from "./sandbox-Dm247NGx.mjs";
2
+ import { a as runOutput, i as run } from "./run-BJLIerMM.mjs";
3
+ import { r as discover } from "./discover-BFoJWKGD.mjs";
5
4
  import { t as appKey } from "./derive-B4vDpzgP.mjs";
6
5
  import { t as defineTarget } from "./define-DR8xGYfL.mjs";
7
- import { t as initStageSecrets } from "./storage-BzaUxmqe.mjs";
8
- import { f as imageRef } from "./store-CLiHnqOY.mjs";
9
- import { $ as portKeys, B as networkEnv, C as isMainFrontendApp, D as constructGlobs, F as postgresStatements, G as hostFor, H as localRolePassword, L as composeFor, N as applyPostgres, R as pgClient, S as validateImageRef, U as planFor, V as envFor, X as DEFAULT_IMAGES, b as generateDockerignore, et as primaryPortKey, f as DeployJournal, g as appPackageName, p as withGeneratedSecrets, s as setDeployedImage, t as createEmptyState, v as siteDockerfile, w as resolveHost, x as generateSlimDockerfile, y as detectPackageManager, z as appEnvKeys } from "./state-B8YwmUZU.mjs";
6
+ import { B as pgClient, C as resolveHost, Et as imageRef, F as applyPostgres, G as planFor, H as networkEnv, J as composeBuildPaths, L as postgresStatements, Q as findBuildRoot, S as isMainFrontendApp, U as envFor, V as appEnvKeys, W as localRolePassword, X as TURBO_VERSION, Y as imageLayout, a as recordRelease, b as ensureDockerignore, g as credentialsFileContent, h as credentialsBuildArg, jt as initStageSecrets, k as constructGlobs, m as withGeneratedSecrets, ot as portKeys, p as DeployJournal, q as hostFor, rt as DEFAULT_IMAGES, st as primaryPortKey, t as createEmptyState, v as appDockerfile, w as encryptSecrets, x as validateImageRef, z as composeFor } from "./state-DkUStoqJ.mjs";
10
7
  import { n as DEFAULT_CACHE, r as DEFAULT_EVENTS } from "./fakes-C9RiT6XG.mjs";
11
- import { createRequire } from "node:module";
12
- import { dirname, extname, join } from "node:path";
13
- import { existsSync } from "node:fs";
14
- import { appendFile, chmod, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
15
- import { DATABASE_FOLDERS, MIGRATIONS_ROOT, canonicalId, migrationTargets, provideKey, provisionOrder, publicEnvFor } from "@geekmidas/manifest";
16
- import { fileURLToPath, pathToFileURL } from "node:url";
8
+ import { u as migrateDatabases } from "./databases-hUHTuwhx.mjs";
9
+ import { dirname, join } from "node:path";
10
+ import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
11
+ import { provideKey, provisionOrder, publicEnvFor } from "@geekmidas/manifest";
17
12
  import { spawn } from "node:child_process";
18
- import pg from "pg";
19
13
  import { stringify } from "yaml";
20
14
  import { request } from "node:https";
21
15
  //#region src/compose/docker.ts
@@ -536,6 +530,7 @@ function composeStack(input) {
536
530
  };
537
531
  for (const [key, value] of Object.entries(app.env)) if (/[\r\n]/.test(value)) throw new EnvValueMultiline(app.name, key);
538
532
  }
533
+ const layout = input.layout ?? defaultLayout(workspace.root);
539
534
  const compose = stackFile({
540
535
  project,
541
536
  plan,
@@ -543,7 +538,10 @@ function composeStack(input) {
543
538
  apps,
544
539
  master: credential.master,
545
540
  https,
546
- http
541
+ http,
542
+ composeDir: join(workspace.root, stackDir(stage)),
543
+ buildRoot: layout.buildRoot,
544
+ workspaceRoot: workspace.root
547
545
  });
548
546
  const caddyfile = edgeCaddyfile(apps.map((app) => ({
549
547
  host: app.host,
@@ -552,18 +550,10 @@ function composeStack(input) {
552
550
  const dockerfiles = {};
553
551
  for (const app of apps) {
554
552
  if (!app.build) continue;
555
- dockerfiles[app.build.dockerfile] = app.kind === "site" ? siteDockerfile(app.name, workspace.apps[app.name], {
556
- turboPackage: input.packages?.[app.name] ?? app.name,
557
- packageManager: input.packageManager ?? "pnpm",
553
+ dockerfiles[app.build.dockerfile] = appDockerfile(app.name, workspace.apps[app.name], {
554
+ layout,
555
+ workspaceRoot: workspace.root,
558
556
  manifest
559
- }) : generateSlimDockerfile({
560
- imageName: app.name,
561
- baseImage: "node:22-alpine",
562
- port: app.port,
563
- healthCheckPath: "/health",
564
- prebuilt: true,
565
- packageManager: input.packageManager ?? "pnpm",
566
- bundle: `${app.path}/.gkm/server/dist/server.mjs`
567
557
  });
568
558
  }
569
559
  return {
@@ -664,8 +654,12 @@ function stackFile(options) {
664
654
  for (const app of apps) services[app.name] = {
665
655
  image: app.ref,
666
656
  ...app.build ? { build: {
667
- context: "../../..",
668
- dockerfile: app.build.dockerfile,
657
+ ...composeBuildPaths({
658
+ composeDir: options.composeDir,
659
+ buildRoot: options.buildRoot,
660
+ workspaceRoot: options.workspaceRoot,
661
+ dockerfile: app.build.dockerfile
662
+ }),
669
663
  ...app.build.args ? { args: app.build.args } : {}
670
664
  } } : {},
671
665
  restart: "unless-stopped",
@@ -705,477 +699,83 @@ function stackFile(options) {
705
699
  volumes
706
700
  };
707
701
  }
708
- /** Render an app's env file: one `KEY=value` per line, read raw by compose. */
709
- function envFile(env) {
710
- return `${Object.entries(env).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${key}=${value}`).join("\n")}\n`;
711
- }
712
- //#endregion
713
- //#region src/migrate/provider.ts
714
- /**
715
- * One construct's migrations and seeds, read from its folders.
716
- *
717
- * Two kinds of file, told apart by extension rather than by configuration: a
718
- * script exports `up` (and may export `down`) — or `seed(db, { stage })`, in
719
- * `seeds/` — and a
720
- * `.sql` file is run whole. The second is what makes a folder independent of
721
- * the client the app uses — Better Auth's schema is SQL whatever the
722
- * application queries with.
723
- */
724
- const SCRIPTS = /* @__PURE__ */ new Set([
725
- ".ts",
726
- ".mts",
727
- ".js",
728
- ".mjs"
729
- ]);
730
- /** Two files in one folder that would be one migration. */
731
- var DuplicateMigration = class extends Error {
732
- folder;
733
- migration;
734
- constructor(folder, migration) {
735
- super(`${folder} has two migrations named '${migration}' — a script and a .sql file with the same name. A migration is recorded by name, so only one of them could ever run; rename or remove the other.`);
736
- this.folder = folder;
737
- this.migration = migration;
738
- this.name = "DuplicateMigration";
739
- }
740
- };
741
- /** A script in a migrations folder that exports no `up`. */
742
- var MigrationHasNoUp = class extends Error {
743
- file;
744
- constructor(file) {
745
- super(`${file} is in a migrations folder but exports no \`up\` function. Export \`async function up(db: Kysely<unknown>)\`, or move the file out of the folder if it is not a migration.`);
746
- this.file = file;
747
- this.name = "MigrationHasNoUp";
748
- }
749
- };
750
- /** A script in a seeds folder that exports no `seed`. */
751
- var SeedHasNoSeed = class extends Error {
752
- file;
753
- constructor(file) {
754
- super(`${file} is in a seeds folder but exports no \`seed\` function. Export \`async function seed(db, { stage })\`, or move the file out of the folder if it is not a seed.`);
755
- this.file = file;
756
- this.name = "SeedHasNoSeed";
757
- }
758
- };
759
- /**
760
- * The migration files in a folder, by name, without reading them.
761
- *
762
- * Also what `gkm dev` compares against a construct's history to report what
763
- * is pending, which is why it is separate from loading them.
764
- */
765
- async function migrationFiles(folder) {
766
- const entries = await readdir(folder, { withFileTypes: true }).catch((error) => {
767
- if (error.code === "ENOENT") return [];
768
- throw error;
769
- });
770
- const files = /* @__PURE__ */ new Map();
771
- for (const entry of entries) {
772
- if (!entry.isFile() || !isMigration(entry.name)) continue;
773
- const name = entry.name.slice(0, -extname(entry.name).length);
774
- if (files.has(name)) throw new DuplicateMigration(folder, name);
775
- files.set(name, join(folder, entry.name));
776
- }
777
- return files;
778
- }
779
- /** A Kysely provider over one construct's folder. */
780
- function folderProvider(folder, raw) {
781
- return { async getMigrations() {
782
- const migrations = {};
783
- for (const [name, file] of await migrationFiles(folder)) migrations[name] = extname(file) === ".sql" ? await sqlMigration(file, raw) : await scriptMigration(file);
784
- return migrations;
785
- } };
786
- }
787
- /**
788
- * The seeds in a folder, loaded, in name order.
789
- *
790
- * Every one runs on every pass — there is no history to consult — so the order
791
- * is the only thing between two seeds where one reads what another writes.
792
- * Name them to sort that way.
793
- */
794
- async function loadSeeds(folder, raw) {
795
- const files = [...await migrationFiles(folder)].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
796
- const seeds = [];
797
- for (const [name, file] of files) seeds.push({
798
- name,
799
- run: extname(file) === ".sql" ? (await sqlMigration(file, raw)).up : await scriptSeed(file)
800
- });
801
- return seeds;
802
- }
803
702
  /**
804
- * A seed script, imported as it is on disk now.
805
- *
806
- * Keyed by its modification time: a seed is run again after it is edited, and
807
- * a process that has imported it once would otherwise run the old one.
703
+ * A build without a layout: the workspace is the build root, with pnpm.
704
+ * What a caller that never looked at the filesystem gets.
808
705
  */
809
- async function scriptSeed(file) {
810
- const url = pathToFileURL(file);
811
- url.searchParams.set("mtime", String((await stat(file)).mtimeMs));
812
- const module = await import(url.href);
813
- if (typeof module.seed !== "function") throw new SeedHasNoSeed(file);
814
- return module.seed;
815
- }
816
- function isMigration(file) {
817
- if (file.endsWith(".d.ts")) return false;
818
- if (/\.(spec|test)\.[cm]?[jt]s$/.test(file)) return false;
819
- const extension = extname(file);
820
- return extension === ".sql" || SCRIPTS.has(extension);
821
- }
822
- async function scriptMigration(file) {
823
- const module = await import(pathToFileURL(file).href);
824
- if (typeof module.up !== "function") throw new MigrationHasNoUp(file);
706
+ function defaultLayout(root) {
825
707
  return {
826
- up: module.up,
827
- ...typeof module.down === "function" ? { down: module.down } : {}
708
+ buildRoot: root,
709
+ gkmRoot: ".",
710
+ tools: {
711
+ packageManager: "pnpm",
712
+ turboVersion: TURBO_VERSION,
713
+ monorepo: true
714
+ },
715
+ gkmPaths: ["gkm.config.*"]
828
716
  };
829
717
  }
830
- /**
831
- * A `.sql` file, run whole.
832
- *
833
- * One query rather than split on `;`: with no parameters, Postgres takes
834
- * several statements in one message, and splitting by hand breaks on the first
835
- * `;` inside a string or a function body. There is no `down` — a generated
836
- * schema is rolled back by a migration that says how, not by guessing.
837
- */
838
- async function sqlMigration(file, raw) {
839
- const statements = await readFile(file, "utf-8");
840
- return { up: async (db) => {
841
- await raw.raw(statements).execute(db);
842
- } };
718
+ /** The id the stack's compose file names an app's build credentials by. */
719
+ function credentialsSecret(app) {
720
+ return `${app}_credentials`;
843
721
  }
844
- //#endregion
845
- //#region src/migrate/databases.ts
846
- /**
847
- * Bring each construct's schema up to its migrations folder.
848
- *
849
- * Which constructs, in what order, from which folder, is `migrationTargets`'
850
- * answer and nobody else's. What is decided here is only how one target is
851
- * run: as its owner, in its own schema, with a history of its own.
852
- */
853
- /** A folder under `db/` that names no construct. */
854
- var UnknownMigrationFolder = class extends Error {
855
- folder;
856
- known;
857
- constructor(folder, known) {
858
- super(`${folder} names no database construct, so its migrations would never run. ${known.length > 0 ? `The folders that do: ${known.join(", ")}.` : "This project declares no database."} Rename the folder after the construct it belongs to.`);
859
- this.folder = folder;
860
- this.known = known;
861
- this.name = "UnknownMigrationFolder";
862
- }
863
- };
864
- /** A construct was asked for by a name that takes no migrations. */
865
- var NoSuchMigrationTarget = class extends Error {
866
- construct;
867
- known;
868
- constructor(construct, known) {
869
- super(`'${construct}' is not a database construct. ` + (known.length > 0 ? `The ones with migrations: ${known.join(", ")}.` : "This project declares no database."));
870
- this.construct = construct;
871
- this.known = known;
872
- this.name = "NoSuchMigrationTarget";
873
- }
874
- };
875
- /**
876
- * A migration left directly in a construct's folder, where nothing reads it.
877
- *
878
- * Migrations moved into `migrations/` so seeds could sit beside them. A file
879
- * still at the old level would otherwise never run, and nothing would say so.
880
- */
881
- var MigrationsOutsideFolder = class extends Error {
882
- folder;
883
- files;
884
- constructor(folder, files) {
885
- super(`${folder} has ${files.join(", ")} directly inside it, where nothing runs them. Migrations live in ${folder}/migrations/ and seeds in ${folder}/seeds/ — move ${files.length === 1 ? "it" : "them"} there.`);
886
- this.folder = folder;
887
- this.files = files;
888
- this.name = "MigrationsOutsideFolder";
889
- }
890
- };
891
- /** A folder inside a construct's folder that is neither `migrations` nor `seeds`. */
892
- var UnknownDatabaseFolder = class extends Error {
893
- folder;
894
- constructor(folder) {
895
- super(`${folder} is neither migrations/ nor seeds/, so nothing in it would ever run. A construct's folder holds those two; rename or remove it.`);
896
- this.folder = folder;
897
- this.name = "UnknownDatabaseFolder";
898
- }
899
- };
900
- /** A seed failed; its own writes are rolled back, the ones before it stay. */
901
- var SeedFailed = class extends Error {
902
- construct;
903
- seed;
904
- cause;
905
- constructor(construct, seed, cause) {
906
- super(`${construct}: seed '${seed}' failed — ${cause instanceof Error ? cause.message : String(cause)}. Its writes were rolled back; fix it and run it again.`);
907
- this.construct = construct;
908
- this.seed = seed;
909
- this.cause = cause;
910
- this.name = "SeedFailed";
911
- }
912
- };
913
- /** A construct with roles has no owner URL to migrate with. */
914
- var NoOwnerCredential = class extends Error {
915
- construct;
916
- key;
917
- constructor(construct, key) {
918
- super(`${construct} has no ${key} to migrate with. Migrations run as the owner role — the runtime one cannot create a table — and nothing resolved its URL. Is the stage reconciled?`);
919
- this.construct = construct;
920
- this.key = key;
921
- this.name = "NoOwnerCredential";
922
- }
923
- };
924
- /** A migration failed; the ones before it in the run stay applied. */
925
- var MigrationFailed = class extends Error {
926
- construct;
927
- migration;
928
- cause;
929
- constructor(construct, migration, cause) {
930
- super(`${construct}: migration ${migration ? `'${migration}' ` : ""}failed — ${cause instanceof Error ? cause.message : String(cause)}`);
931
- this.construct = construct;
932
- this.migration = migration;
933
- this.cause = cause;
934
- this.name = "MigrationFailed";
935
- }
936
- };
937
- /** The project's Kysely could not be found from where a database is declared. */
938
- var KyselyNotFound = class extends Error {
939
- from;
940
- constructor(from) {
941
- super(`Could not resolve 'kysely' from ${from}. Migrations run with the project's own Kysely — install it where the database is declared.`);
942
- this.from = from;
943
- this.name = "KyselyNotFound";
944
- }
945
- };
946
- /** Apply every pending migration, construct by construct, parents first. */
947
- async function migrateDatabases(options) {
948
- const runs = [];
949
- for (const target of await targetsFor(options)) {
950
- if ((await migrationFiles(join(options.root, target.migrations))).size === 0) {
951
- runs.push({
952
- target,
953
- applied: []
954
- });
955
- continue;
956
- }
957
- const applied = await withMigrator(options, target, async (migrator) => {
958
- const { error, results = [] } = await migrator.migrateToLatest();
959
- if (error) {
960
- const failed = results.find((result) => result.status === "Error");
961
- throw new MigrationFailed(target.id, failed?.migrationName, error);
962
- }
963
- return results.filter((result) => result.status === "Success").map((result) => result.migrationName);
964
- });
965
- runs.push({
966
- target,
967
- applied
968
- });
969
- }
970
- return runs;
722
+ /** The file beside the compose file an app's build credentials are read from. */
723
+ function credentialsFile(app) {
724
+ return `${app}.credentials`;
971
725
  }
972
726
  /**
973
- * Run every construct's seeds, parents first — each one, every time, on every
974
- * stage, production included. Each is handed the stage, so a seed decides
975
- * what belongs where.
727
+ * The stack with each backend's image built with its encrypted credentials:
728
+ * a BuildKit secret per app — the file `credentialsFile` names, mounted as
729
+ * `gkm_credentials` — and the hash naming them as a build arg, so a new key
730
+ * rebuilds the layer that embeds them. Each backend's env file gains the
731
+ * `GKM_MASTER_KEY` that decrypts them, the way a Dokploy deploy injects it.
976
732
  *
977
- * Seeds are reference data kept up to date by being run again, so there is no
978
- * history: a seed is an upsert, and a changed one is applied by the next run.
979
- * Each runs in its own transaction, as the construct's owner, so a failure
980
- * leaves nothing half-written. Assumes the schema is migrated —
981
- * {@link migrateAndSeed} is what makes that true.
733
+ * Pure: the caller encrypted them.
982
734
  */
983
- async function seedDatabases(options) {
984
- const runs = [];
985
- for (const target of await targetsFor(options)) {
986
- const folder = join(options.root, target.seeds);
987
- if ((await migrationFiles(folder)).size === 0) {
988
- runs.push({
989
- target,
990
- seeded: []
991
- });
992
- continue;
993
- }
994
- const seeded = await withOwner(options, target, async (db, kysely) => {
995
- const names = [];
996
- for (const seed of await loadSeeds(folder, kysely.sql)) {
997
- try {
998
- await db.transaction().execute((trx) => seed.run(trx, { stage: options.stage }));
999
- } catch (error) {
1000
- throw new SeedFailed(target.id, seed.name, error);
1001
- }
1002
- names.push(seed.name);
735
+ function withBuildCredentials(stack, credentials) {
736
+ const services = { ...stack.compose.services };
737
+ const secrets = { ...stack.compose.secrets };
738
+ const apps = stack.apps.map((app) => {
739
+ const own = credentials[app.name];
740
+ const service = services[app.name];
741
+ if (!own || !service?.build) return app;
742
+ const [key, value] = own.buildArg.split("=");
743
+ services[app.name] = {
744
+ ...service,
745
+ build: {
746
+ ...service.build,
747
+ args: {
748
+ ...service.build.args,
749
+ [key]: value
750
+ },
751
+ secrets: [{
752
+ source: credentialsSecret(app.name),
753
+ target: "gkm_credentials"
754
+ }]
1003
755
  }
1004
- return names;
1005
- });
1006
- runs.push({
1007
- target,
1008
- seeded
1009
- });
1010
- }
1011
- return runs;
1012
- }
1013
- /**
1014
- * Migrate, then seed: a seed only ever runs against the schema it was written
1015
- * for.
1016
- *
1017
- * With `only`, the constructs it lives in are migrated too — a tenant's seed
1018
- * cannot run before the database that owns its schema exists — and only it is
1019
- * seeded.
1020
- */
1021
- async function migrateAndSeed(options) {
1022
- const migrations = [];
1023
- for (const target of await withParents(options)) migrations.push(...await migrateDatabases({
1024
- ...options,
1025
- only: target.id
1026
- }));
756
+ };
757
+ secrets[credentialsSecret(app.name)] = { file: `./${credentialsFile(app.name)}` };
758
+ return {
759
+ ...app,
760
+ env: {
761
+ ...app.env,
762
+ GKM_MASTER_KEY: own.masterKey
763
+ }
764
+ };
765
+ });
1027
766
  return {
1028
- migrations,
1029
- seeds: await seedDatabases(options)
1030
- };
1031
- }
1032
- /** The targets `only` names, after the ones it lives in; all without it. */
1033
- async function withParents(options) {
1034
- const chosen = await targetsFor(options);
1035
- if (!options.only) return chosen;
1036
- const all = migrationTargets(options.manifest);
1037
- const ids = /* @__PURE__ */ new Set();
1038
- for (const target of chosen) {
1039
- let current = target;
1040
- while (current) {
1041
- ids.add(current.id);
1042
- const parent = current.of;
1043
- current = parent ? all.find((t) => t.id === parent) : void 0;
767
+ ...stack,
768
+ apps,
769
+ compose: {
770
+ ...stack.compose,
771
+ services,
772
+ ...Object.keys(secrets).length ? { secrets } : {}
1044
773
  }
1045
- }
1046
- return all.filter((target) => ids.has(target.id));
1047
- }
1048
- /**
1049
- * What each construct's folder has that its history does not — without
1050
- * applying any of it. What `gkm dev` reports.
1051
- */
1052
- async function pendingMigrations(options) {
1053
- const found = [];
1054
- for (const target of await targetsFor(options)) {
1055
- if ((await migrationFiles(join(options.root, target.migrations))).size === 0) continue;
1056
- const pending = await withMigrator(options, target, async (migrator) => (await migrator.getMigrations()).filter((migration) => migration.executedAt === void 0).map((migration) => migration.name));
1057
- if (pending.length > 0) found.push({
1058
- target,
1059
- pending
1060
- });
1061
- }
1062
- return found;
1063
- }
1064
- /**
1065
- * The targets this run covers, with every folder under `db/` accounted for.
1066
- *
1067
- * A folder that names no construct is an error rather than something to skip:
1068
- * a typo in its name is otherwise a migration that silently never runs.
1069
- */
1070
- async function targetsFor(options) {
1071
- const targets = migrationTargets(options.manifest);
1072
- const folders = targets.map((target) => target.folder);
1073
- for (const folder of await foldersIn(join(options.root, MIGRATIONS_ROOT))) {
1074
- const relative = `${MIGRATIONS_ROOT}/${folder}`;
1075
- if (!folders.includes(relative)) throw new UnknownMigrationFolder(relative, folders);
1076
- await checkLayout(options.root, relative);
1077
- }
1078
- if (!options.only) return targets;
1079
- const id = canonicalId(options.only);
1080
- const chosen = targets.filter((target) => target.id === id);
1081
- if (chosen.length === 0) throw new NoSuchMigrationTarget(options.only, targets.map((target) => target.id));
1082
- return chosen;
1083
- }
1084
- /**
1085
- * A construct's folder holds `migrations/` and `seeds/`, and nothing else —
1086
- * anything beside them is a file or folder nothing would ever run.
1087
- */
1088
- async function checkLayout(root, folder) {
1089
- const entries = await readdir(join(root, folder), { withFileTypes: true });
1090
- const loose = entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).map((entry) => entry.name);
1091
- if (loose.length > 0) throw new MigrationsOutsideFolder(folder, loose);
1092
- for (const entry of entries) if (entry.isDirectory() && !DATABASE_FOLDERS.includes(entry.name)) throw new UnknownDatabaseFolder(`${folder}/${entry.name}`);
1093
- }
1094
- async function foldersIn(dir) {
1095
- return (await readdir(dir, { withFileTypes: true }).catch((error) => {
1096
- if (error.code === "ENOENT") return [];
1097
- throw error;
1098
- })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
1099
- }
1100
- /**
1101
- * A migrator for one target, connected as its owner.
1102
- *
1103
- * No `migrationTableSchema`: the owner role's `search_path` is pinned to the
1104
- * construct's schema, so the history lands there — `app.kysely_migration` for
1105
- * the database, `auth_database.kysely_migration` for its tenant — and two
1106
- * constructs in one Postgres never share one.
1107
- *
1108
- * Unordered migrations are allowed. Files are timestamped, so a branch merged
1109
- * after another's migration ran carries an older one; refusing it would make
1110
- * every such merge a manual repair.
1111
- */
1112
- async function withMigrator(options, target, run) {
1113
- return withOwner(options, target, (db, { Migrator, sql }) => run(new Migrator({
1114
- db,
1115
- provider: folderProvider(join(options.root, target.migrations), sql),
1116
- allowUnorderedMigrations: true
1117
- })));
1118
- }
1119
- /**
1120
- * One target's database, connected as its owner and closed afterwards — what
1121
- * migrations and seeds both run as.
1122
- */
1123
- async function withOwner(options, target, run) {
1124
- const kysely = await kyselyFrom(options.sources?.[target.id]?.file ?? (target.of ? options.sources?.[target.of]?.file : void 0) ?? join(options.root, "package.json"));
1125
- const db = new kysely.Kysely({ dialect: new kysely.PostgresDialect({ pool: new pg.Pool(poolConfig(ownerUrl(options, target))) }) });
1126
- try {
1127
- return await run(db, kysely);
1128
- } finally {
1129
- await db.destroy();
1130
- }
1131
- }
1132
- /**
1133
- * The owner's URL, with no fallback to the runtime one.
1134
- *
1135
- * The runtime role can create nothing, so falling back would only move the
1136
- * failure to the first `CREATE TABLE` and make it read like a permissions
1137
- * bug. The one exception is a database declared `roles: false`, where there is
1138
- * no owner — both URLs are the master credential by design.
1139
- */
1140
- function ownerUrl(options, target) {
1141
- const key = provideKey(target.id, "ownerUrl");
1142
- const owner = options.env[key];
1143
- if (owner) return owner;
1144
- const database = options.manifest[target.of ?? target.id];
1145
- if (database?.kind === "database" && database.roles === false) {
1146
- const url = options.env[provideKey(target.id, "url")];
1147
- if (url) return url;
1148
- }
1149
- throw new NoOwnerCredential(target.id, key);
1150
- }
1151
- /**
1152
- * Pool settings for a URL, with `?search_path=` as the startup option Postgres
1153
- * reads. Only a `roles: false` URL carries it — a role pins its own.
1154
- */
1155
- function poolConfig(url) {
1156
- const parsed = new URL(url);
1157
- const searchPath = parsed.searchParams.get("search_path");
1158
- if (!searchPath) return { connectionString: url };
1159
- parsed.searchParams.delete("search_path");
1160
- return {
1161
- connectionString: parsed.toString(),
1162
- options: `-c search_path=${searchPath}`
1163
774
  };
1164
775
  }
1165
- /** Kysely as the project resolves it from `from`. */
1166
- async function kyselyFrom(from) {
1167
- const require = createRequire(from);
1168
- try {
1169
- const [main, migration] = await Promise.all([import(pathToFileURL(require.resolve("kysely")).href), import(pathToFileURL(require.resolve("kysely/migration")).href)]);
1170
- return {
1171
- Kysely: main.Kysely,
1172
- PostgresDialect: main.PostgresDialect,
1173
- sql: main.sql,
1174
- Migrator: migration.Migrator
1175
- };
1176
- } catch {
1177
- throw new KyselyNotFound(from);
1178
- }
776
+ /** Render an app's env file: one `KEY=value` per line, read raw by compose. */
777
+ function envFile(env) {
778
+ return `${Object.entries(env).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${key}=${value}`).join("\n")}\n`;
1179
779
  }
1180
780
  //#endregion
1181
781
  //#region src/target/compose/health.ts
@@ -1260,7 +860,11 @@ var ComposeAppsUnhealthy = class extends Error {
1260
860
  * - What was started answers (`verify`): each app is asked through Caddy,
1261
861
  * over HTTPS, with the certificate verified.
1262
862
  *
1263
- * Docker, the registry, the bundler, Postgres and the health probe are
863
+ * Every image is built inside Docker from a pruned slice of the build root —
864
+ * the same Dockerfiles `gkm docker` writes — so nothing is built on this
865
+ * machine before `docker compose build`.
866
+ *
867
+ * Docker, the registry, Postgres and the health probe are
1264
868
  * injected, so the rules are asserted without a daemon; the defaults are the
1265
869
  * real ones.
1266
870
  */
@@ -1284,28 +888,6 @@ var NoGitRevision = class extends Error {
1284
888
  this.name = "NoGitRevision";
1285
889
  }
1286
890
  };
1287
- /** A backend's production bundle did not build. */
1288
- var BundleFailed = class extends Error {
1289
- app;
1290
- exitCode;
1291
- stderr;
1292
- constructor(app, exitCode, stderr) {
1293
- super(`Bundling '${app}' for production failed (exit code ${exitCode}).${stderr.trim() ? `\n${stderr.trim().slice(-4e3)}` : ""}\nRun \`gkm build --provider server --production\` in the app's directory to see it again.`);
1294
- this.app = app;
1295
- this.exitCode = exitCode;
1296
- this.stderr = stderr;
1297
- this.name = "BundleFailed";
1298
- }
1299
- };
1300
- /** The CLI's own entry, which the bundle step runs, is not where it should be. */
1301
- var CliEntryNotFound = class extends Error {
1302
- from;
1303
- constructor(from) {
1304
- super(`Could not find bin/gkm.mjs above ${from}, which bundling a backend runs. Reinstall @geekmidas/cli.`);
1305
- this.from = from;
1306
- this.name = "CliEntryNotFound";
1307
- }
1308
- };
1309
891
  /** The commit HEAD is at, short — and `-dirty` when the tree has changes. */
1310
892
  async function gitRevision(root) {
1311
893
  let sha;
@@ -1335,46 +917,12 @@ function edgePorts(env = process.env) {
1335
917
  http: read(EDGE_PORT_ENV.http, 80)
1336
918
  };
1337
919
  }
1338
- /** `bin/gkm.mjs` of the CLI that is running. */
1339
- function cliEntry() {
1340
- const from = dirname(fileURLToPath(import.meta.url));
1341
- for (let dir = from; dirname(dir) !== dir; dir = dirname(dir)) {
1342
- const entry = join(dir, "bin", "gkm.mjs");
1343
- if (existsSync(entry)) return entry;
1344
- }
1345
- throw new CliEntryNotFound(from);
1346
- }
1347
- /** A cold production bundle is seconds; a hung one must not hold the lock. */
1348
- const BUNDLE_TIMEOUT_MS = 6e5;
1349
- /**
1350
- * Bundle a backend with the gkm that is running — the same build a deploy
1351
- * makes, so the image holds one file and no dependencies — in the run's
1352
- * sandbox, because the build imports the project's code.
1353
- */
1354
- async function bundleInSandbox(appRoot, ctx) {
1355
- const sandbox = activeSandbox() ?? new LocalSandbox({ root: ctx.cwd });
1356
- const result = await sandbox.exec("node", [
1357
- cliEntry(),
1358
- "build",
1359
- "--provider",
1360
- "server",
1361
- "--production"
1362
- ], {
1363
- cwd: appRoot,
1364
- env: sandbox.env,
1365
- timeoutMs: BUNDLE_TIMEOUT_MS,
1366
- output: ctx.childOutput,
1367
- signal: ctx.signal
1368
- });
1369
- if (result.exitCode !== 0) throw new BundleFailed(appRoot, result.exitCode, result.stderr);
1370
- }
1371
920
  /**
1372
921
  * The real ones. Each app is asked for three minutes before it counts as
1373
922
  * down: a deployed stage's first ACME certificate can take that long.
1374
923
  */
1375
924
  const defaultDeps = {
1376
925
  docker: dockerCompose,
1377
- bundle: bundleInSandbox,
1378
926
  revision: gitRevision,
1379
927
  sql: pgClient,
1380
928
  migrate: migrateDatabases,
@@ -1399,7 +947,9 @@ async function validateCompose(ctx, deps, forced) {
1399
947
  runnables
1400
948
  });
1401
949
  const { secrets, generated } = await stageSecrets(ctx, manifest);
1402
- const stack = composeStack({
950
+ const masterKeys = {};
951
+ const layout = mode === "build" ? imageLayout(workspace) : void 0;
952
+ const composed = composeStack({
1403
953
  workspace,
1404
954
  manifest,
1405
955
  runnables,
@@ -1412,12 +962,36 @@ async function validateCompose(ctx, deps, forced) {
1412
962
  },
1413
963
  secrets,
1414
964
  ports: edgePorts(deps.env),
1415
- packageManager: detectPackageManager(root),
1416
- packages: Object.fromEntries(Object.entries(workspace.apps).map(([name, app]) => [name, appPackageName(root, name, app.path)]))
965
+ ...layout ? { layout } : {}
1417
966
  });
967
+ const credentials = {};
968
+ if (mode === "build") for (const app of composed.apps) {
969
+ if (app.kind !== "rest-api" || !app.env || !app.build) continue;
970
+ const { encrypted, iv, masterKey } = encryptSecrets(app.env);
971
+ credentials[app.name] = {
972
+ encrypted,
973
+ iv
974
+ };
975
+ ctx.secrets.mask(masterKey);
976
+ masterKeys[app.name] = {
977
+ masterKey,
978
+ buildArg: credentialsBuildArg({
979
+ encrypted,
980
+ iv
981
+ })
982
+ };
983
+ }
984
+ const stack = withBuildCredentials(composed, masterKeys);
1418
985
  if (mode === "pull" && !ctx.dryRun) await assertImagesExist(deps.docker, tag, stack.apps);
1419
986
  const dir = join(root, stackDir(stage));
1420
- const previous = (await ctx.state.read(stage))?.state.images ?? {};
987
+ const previous = Object.fromEntries(Object.entries((await ctx.state.read(stage))?.state.releases ?? {}).map(([name, releases]) => {
988
+ const { ref, tag, digest } = releases.current;
989
+ return [name, {
990
+ ref,
991
+ ...tag ? { tag } : {},
992
+ ...digest ? { digest } : {}
993
+ }];
994
+ }));
1421
995
  return {
1422
996
  mode,
1423
997
  stack,
@@ -1433,6 +1007,7 @@ async function validateCompose(ctx, deps, forced) {
1433
1007
  generated,
1434
1008
  previous,
1435
1009
  files: [],
1010
+ credentials,
1436
1011
  images: {},
1437
1012
  changes: []
1438
1013
  };
@@ -1464,7 +1039,7 @@ async function stageSecrets(ctx, manifest) {
1464
1039
  */
1465
1040
  async function planCompose(ctx, run) {
1466
1041
  if (run.generated.length > 0) ctx.logger.info(`🔑 "${ctx.stage}" has no ${run.generated.join(", ")} yet; this dry run used values it did not keep.`);
1467
- run.files = await writeStack(ctx.cwd, run.dir, run.stack);
1042
+ run.files = await writeStack(ctx.cwd, run.dir, run.stack, run.credentials);
1468
1043
  printPlan(ctx, run);
1469
1044
  const planned = (change) => {
1470
1045
  run.changes.push(change);
@@ -1508,7 +1083,7 @@ async function provisionCompose(ctx, run, deps) {
1508
1083
  await ctx.secrets.write(run.secrets);
1509
1084
  ctx.logger.info(`🔑 Generated for "${ctx.stage}" (${ctx.secrets.store}): ${run.generated.join(", ")}`);
1510
1085
  }
1511
- run.files = await writeStack(ctx.cwd, run.dir, stack);
1086
+ run.files = await writeStack(ctx.cwd, run.dir, stack, run.credentials);
1512
1087
  printPlan(ctx, run);
1513
1088
  if (stack.infra.length > 0) {
1514
1089
  ctx.logger.info(`\n🗄️ Starting ${stack.infra.join(", ")}…`);
@@ -1554,18 +1129,14 @@ async function prepareDatabases(ctx, run, deps) {
1554
1129
  for (const { target, applied } of runs) if (applied.length > 0) ctx.logger.info(`🗄️ ${target.migrations}: applied ${applied.length}`);
1555
1130
  }
1556
1131
  /**
1557
- * Each app's image: built from this checkout — every backend bundled first,
1558
- * on the host, with this CLI — or pulled at the tag. Nothing live changes.
1132
+ * Each app's image: built from this checkout — inside Docker, every one of
1133
+ * them, from a pruned slice of the build root — or pulled at the tag. Nothing
1134
+ * live changes.
1559
1135
  */
1560
1136
  async function buildCompose(ctx, run, deps) {
1561
1137
  const { stack, ref } = run;
1562
1138
  const apps = stack.apps.map((app) => app.name);
1563
1139
  if (run.mode === "build") {
1564
- for (const app of stack.apps.filter((a) => a.kind === "rest-api")) {
1565
- ctx.signal.throwIfAborted();
1566
- ctx.logger.info(`\n📦 Bundling ${app.name}…`);
1567
- await deps.bundle(join(ctx.cwd, app.path), ctx);
1568
- }
1569
1140
  ctx.logger.info("\n🐳 Building images…");
1570
1141
  await deps.docker.build(ref, apps);
1571
1142
  } else {
@@ -1636,7 +1207,7 @@ async function recordImages(ctx, run) {
1636
1207
  ref: app.ref,
1637
1208
  tag: app.tag
1638
1209
  };
1639
- setDeployedImage(journal.state, app.name, image);
1210
+ recordRelease(journal.state, app.name, image);
1640
1211
  }
1641
1212
  journal.state.identity = ctx.identity.key;
1642
1213
  await journal.save();
@@ -1748,7 +1319,7 @@ function applied(ctx, run, change) {
1748
1319
  });
1749
1320
  }
1750
1321
  /** Write the stack's files: owner-only, since the env files hold secrets. */
1751
- async function writeStack(root, dir, stack) {
1322
+ async function writeStack(root, dir, stack, credentials = {}) {
1752
1323
  await mkdir(dir, {
1753
1324
  recursive: true,
1754
1325
  mode: 448
@@ -1764,21 +1335,11 @@ async function writeStack(root, dir, stack) {
1764
1335
  await write(join(dir, "docker-compose.yml"), composeYaml(stack));
1765
1336
  await write(join(dir, "Caddyfile"), stack.caddyfile);
1766
1337
  for (const app of stack.apps) if (app.env) await write(join(dir, `${app.name}.env`), envFile(app.env), 384);
1338
+ for (const [name, payload] of Object.entries(credentials)) await write(join(dir, credentialsFile(name)), credentialsFileContent(payload), 384);
1767
1339
  for (const [path, content] of Object.entries(stack.dockerfiles)) await write(join(root, path), content);
1768
- await ignoreStacks(root);
1340
+ await ensureDockerignore(findBuildRoot(root));
1769
1341
  return files;
1770
1342
  }
1771
- /** Make sure the build context leaves `.gkm/compose` out. */
1772
- async function ignoreStacks(root) {
1773
- const path = join(root, ".dockerignore");
1774
- if (!existsSync(path)) {
1775
- await writeFile(path, generateDockerignore());
1776
- return;
1777
- }
1778
- const current = await readFile(path, "utf-8");
1779
- if (/^\/?\.gkm\/compose\/?$|^\/?\.gkm\/?$/m.test(current)) return;
1780
- await appendFile(path, `${current.endsWith("\n") ? "" : "\n"}\n# gkm compose's stacks: each app's env file holds its stage's secrets\n.gkm/compose\n`);
1781
- }
1782
1343
  function composeYaml(stack) {
1783
1344
  const stage = stack.local ? "" : ` --stage ${stack.stage}`;
1784
1345
  return `# Generated by gkm compose from the construct manifest — do not edit.
@@ -1857,6 +1418,6 @@ function composeTarget(options = {}) {
1857
1418
  });
1858
1419
  }
1859
1420
  //#endregion
1860
- export { SeedHasNoSeed as _, MigrationsOutsideFolder as a, dockerCompose as b, SeedFailed as c, migrateAndSeed as d, migrateDatabases as f, MigrationHasNoUp as g, DuplicateMigration as h, MigrationFailed as i, UnknownDatabaseFolder as l, seedDatabases as m, compose_exports as n, NoOwnerCredential as o, pendingMigrations as p, KyselyNotFound as r, NoSuchMigrationTarget as s, composeTarget as t, UnknownMigrationFolder as u, composeProject as v, stackDir as y };
1421
+ export { dockerCompose as a, stackDir as i, compose_exports as n, composeProject as r, composeTarget as t };
1861
1422
 
1862
- //# sourceMappingURL=compose-DprE3ASm.mjs.map
1423
+ //# sourceMappingURL=compose-BjToENzG.mjs.map