@geekmidas/cli 10.0.0-alpha.82 → 10.0.0-alpha.84

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 (169) hide show
  1. package/dist/{EndpointGenerator-DtnIGafh.mjs → EndpointGenerator-CVc6Oi1Y.mjs} +185 -82
  2. package/dist/EndpointGenerator-CVc6Oi1Y.mjs.map +1 -0
  3. package/dist/{EndpointGenerator-cKlnllIu.cjs → EndpointGenerator-CwLjBCFE.cjs} +203 -82
  4. package/dist/EndpointGenerator-CwLjBCFE.cjs.map +1 -0
  5. package/dist/{LegacyStateStore-Ceq7A_75.mjs → LegacyStateStore-CTsUFLNQ.mjs} +2 -2
  6. package/dist/{LegacyStateStore-Ceq7A_75.mjs.map → LegacyStateStore-CTsUFLNQ.mjs.map} +1 -1
  7. package/dist/{LegacyStateStore-Z-EKUGSX.cjs → LegacyStateStore-EDT2TUSe.cjs} +2 -2
  8. package/dist/{LegacyStateStore-Z-EKUGSX.cjs.map → LegacyStateStore-EDT2TUSe.cjs.map} +1 -1
  9. package/dist/{LocalStateStore-Bocb8dmZ.cjs → LocalStateStore-CdpzTvPn.cjs} +2 -2
  10. package/dist/{LocalStateStore-Bocb8dmZ.cjs.map → LocalStateStore-CdpzTvPn.cjs.map} +1 -1
  11. package/dist/{LocalStateStore-C72bwpG2.mjs → LocalStateStore-D1VfxRuK.mjs} +2 -2
  12. package/dist/{LocalStateStore-C72bwpG2.mjs.map → LocalStateStore-D1VfxRuK.mjs.map} +1 -1
  13. package/dist/{OpenApiTsGenerator-CdUM6Hvv.cjs → OpenApiTsGenerator-B--HV-sW.cjs} +2 -2
  14. package/dist/{OpenApiTsGenerator-CdUM6Hvv.cjs.map → OpenApiTsGenerator-B--HV-sW.cjs.map} +1 -1
  15. package/dist/{OpenApiTsGenerator-ByRPMbBN.mjs → OpenApiTsGenerator-CkvsPdgg.mjs} +2 -2
  16. package/dist/{OpenApiTsGenerator-ByRPMbBN.mjs.map → OpenApiTsGenerator-CkvsPdgg.mjs.map} +1 -1
  17. package/dist/{S3StateStore-35YfcdL_.cjs → S3StateStore-BPM74zwv.cjs} +2 -2
  18. package/dist/{S3StateStore-35YfcdL_.cjs.map → S3StateStore-BPM74zwv.cjs.map} +1 -1
  19. package/dist/{S3StateStore-C9upSW5o.mjs → S3StateStore-DVZ1YuGJ.mjs} +2 -2
  20. package/dist/{S3StateStore-C9upSW5o.mjs.map → S3StateStore-DVZ1YuGJ.mjs.map} +1 -1
  21. package/dist/{SSMStateStore-dOmFO6g1.mjs → SSMStateStore-DLvnGyyl.mjs} +2 -2
  22. package/dist/{SSMStateStore-dOmFO6g1.mjs.map → SSMStateStore-DLvnGyyl.mjs.map} +1 -1
  23. package/dist/{SSMStateStore-W-Ps22AQ.cjs → SSMStateStore-qerm5c36.cjs} +2 -2
  24. package/dist/{SSMStateStore-W-Ps22AQ.cjs.map → SSMStateStore-qerm5c36.cjs.map} +1 -1
  25. package/dist/{SubscriberGenerator-DQZeGkDW.mjs → SubscriberGenerator-BPqQf8fB.mjs} +2 -2
  26. package/dist/{SubscriberGenerator-DQZeGkDW.mjs.map → SubscriberGenerator-BPqQf8fB.mjs.map} +1 -1
  27. package/dist/{SubscriberGenerator-CYriX5BV.cjs → SubscriberGenerator-BVn7cQ-2.cjs} +2 -2
  28. package/dist/{SubscriberGenerator-CYriX5BV.cjs.map → SubscriberGenerator-BVn7cQ-2.cjs.map} +1 -1
  29. package/dist/{add-1T3y6E4M.cjs → add-BQ8v1ZII.cjs} +3 -3
  30. package/dist/{add-1T3y6E4M.cjs.map → add-BQ8v1ZII.cjs.map} +1 -1
  31. package/dist/{add-DnYHJOap.mjs → add-DbnzC7Kl.mjs} +3 -3
  32. package/dist/{add-DnYHJOap.mjs.map → add-DbnzC7Kl.mjs.map} +1 -1
  33. package/dist/{bundler-DlKdxCGh.mjs → bundler-D4030Afw.mjs} +5 -5
  34. package/dist/{bundler-DlKdxCGh.mjs.map → bundler-D4030Afw.mjs.map} +1 -1
  35. package/dist/{bundler-YNvLdvlh.cjs → bundler-DjrRffko.cjs} +5 -5
  36. package/dist/{bundler-YNvLdvlh.cjs.map → bundler-DjrRffko.cjs.map} +1 -1
  37. package/dist/{cli-CWee1-wU.cjs → cli-D8YQAYUX.cjs} +3 -3
  38. package/dist/{cli-CWee1-wU.cjs.map → cli-D8YQAYUX.cjs.map} +1 -1
  39. package/dist/{cli-qMszGvDu.mjs → cli-DwTa9d_8.mjs} +3 -3
  40. package/dist/{cli-qMszGvDu.mjs.map → cli-DwTa9d_8.mjs.map} +1 -1
  41. package/dist/{compose-D4sD4Lgr.mjs → compose-C5wz8mf9.mjs} +64 -24
  42. package/dist/compose-C5wz8mf9.mjs.map +1 -0
  43. package/dist/{compose-qnDyPJNp.cjs → compose-DqWh1TaO.cjs} +64 -24
  44. package/dist/compose-DqWh1TaO.cjs.map +1 -0
  45. package/dist/{config-Mr4rcXFC.d.cts → config-BzIfP8a-.d.cts} +16 -3
  46. package/dist/config-BzIfP8a-.d.cts.map +1 -0
  47. package/dist/{config-L_6NivCp.d.mts → config-DhdzmndX.d.mts} +16 -3
  48. package/dist/config-DhdzmndX.d.mts.map +1 -0
  49. package/dist/config.cjs +1 -1
  50. package/dist/config.d.cts +3 -3
  51. package/dist/config.d.mts +3 -3
  52. package/dist/config.mjs +1 -1
  53. package/dist/deploy/public.cjs +6 -6
  54. package/dist/deploy/public.d.cts +3 -3
  55. package/dist/deploy/public.d.cts.map +1 -1
  56. package/dist/deploy/public.d.mts +3 -3
  57. package/dist/deploy/public.d.mts.map +1 -1
  58. package/dist/deploy/public.mjs +6 -6
  59. package/dist/{deploy-C00DAKOt.mjs → deploy-BwIKGxWH.mjs} +14 -5
  60. package/dist/deploy-BwIKGxWH.mjs.map +1 -0
  61. package/dist/{deploy-CXP2fXvK.cjs → deploy-C8sWg81_.cjs} +14 -5
  62. package/dist/deploy-C8sWg81_.cjs.map +1 -0
  63. package/dist/{dokploy-BHcSmghb.cjs → dokploy-BkRxJbfi.cjs} +2 -2
  64. package/dist/{dokploy-BHcSmghb.cjs.map → dokploy-BkRxJbfi.cjs.map} +1 -1
  65. package/dist/{dokploy-B3zoCmJb.mjs → dokploy-ijjms1-h.mjs} +2 -2
  66. package/dist/{dokploy-B3zoCmJb.mjs.map → dokploy-ijjms1-h.mjs.map} +1 -1
  67. package/dist/{drivers-B4RP2B2O.mjs → drivers-DTZzxeIl.mjs} +2 -2
  68. package/dist/{drivers-B4RP2B2O.mjs.map → drivers-DTZzxeIl.mjs.map} +1 -1
  69. package/dist/{drivers-CJx76mcb.cjs → drivers-LYSdQDmd.cjs} +2 -2
  70. package/dist/{drivers-CJx76mcb.cjs.map → drivers-LYSdQDmd.cjs.map} +1 -1
  71. package/dist/{encryption-DPFljXtM.mjs → encryption-BRcBAmjL.mjs} +3 -3
  72. package/dist/{encryption-DPFljXtM.mjs.map → encryption-BRcBAmjL.mjs.map} +1 -1
  73. package/dist/{encryption-DLin89I_.cjs → encryption-g6PzwwfL.cjs} +3 -3
  74. package/dist/{encryption-DLin89I_.cjs.map → encryption-g6PzwwfL.cjs.map} +1 -1
  75. package/dist/{engine-CgzgIg-_.mjs → engine-BP0CZ5fh.mjs} +33 -13
  76. package/dist/engine-BP0CZ5fh.mjs.map +1 -0
  77. package/dist/{engine-BS3iX9Mu.cjs → engine-DLEJsZ3i.cjs} +33 -13
  78. package/dist/engine-DLEJsZ3i.cjs.map +1 -0
  79. package/dist/{github-D8KVmNRg.cjs → github-B8YjwdLK.cjs} +3 -3
  80. package/dist/{github-D8KVmNRg.cjs.map → github-B8YjwdLK.cjs.map} +1 -1
  81. package/dist/{github-DL4zwJsR.mjs → github-Zs-dVJbR.mjs} +3 -3
  82. package/dist/{github-DL4zwJsR.mjs.map → github-Zs-dVJbR.mjs.map} +1 -1
  83. package/dist/{index-C0UmP_lg.d.mts → index-D1ODeM9s.d.cts} +7 -3
  84. package/dist/index-D1ODeM9s.d.cts.map +1 -0
  85. package/dist/{index-DTEPwLml.d.cts → index-f77HdqN5.d.mts} +7 -3
  86. package/dist/index-f77HdqN5.d.mts.map +1 -0
  87. package/dist/index.cjs +553 -134
  88. package/dist/index.cjs.map +1 -1
  89. package/dist/index.mjs +553 -134
  90. package/dist/index.mjs.map +1 -1
  91. package/dist/{migrate-DIjG8-QR.cjs → migrate-BfZN1S32.cjs} +3 -3
  92. package/dist/{migrate-DIjG8-QR.cjs.map → migrate-BfZN1S32.cjs.map} +1 -1
  93. package/dist/{migrate-CCejlgU0.mjs → migrate-CdXcX0XR.mjs} +3 -3
  94. package/dist/{migrate-CCejlgU0.mjs.map → migrate-CdXcX0XR.mjs.map} +1 -1
  95. package/dist/{migrate-Cd3kcI7o.mjs → migrate-DOMU4uKe.mjs} +3 -3
  96. package/dist/{migrate-Cd3kcI7o.mjs.map → migrate-DOMU4uKe.mjs.map} +1 -1
  97. package/dist/{migrate-BdKhIJgT.cjs → migrate-FoL_mZIz.cjs} +3 -3
  98. package/dist/{migrate-BdKhIJgT.cjs.map → migrate-FoL_mZIz.cjs.map} +1 -1
  99. package/dist/openapi.cjs +3 -3
  100. package/dist/openapi.d.cts +1 -1
  101. package/dist/openapi.d.mts +1 -1
  102. package/dist/openapi.mjs +3 -3
  103. package/dist/{ready-4kx2pAhm.cjs → ready-BpNbORRB.cjs} +5 -5
  104. package/dist/{ready-4kx2pAhm.cjs.map → ready-BpNbORRB.cjs.map} +1 -1
  105. package/dist/{ready-DhZbZtFw.mjs → ready-b6vm0-W-.mjs} +5 -5
  106. package/dist/{ready-DhZbZtFw.mjs.map → ready-b6vm0-W-.mjs.map} +1 -1
  107. package/dist/reconcile/public.d.cts +1 -1
  108. package/dist/reconcile/public.d.mts +1 -1
  109. package/dist/{resolve-ApSUK_HB.cjs → resolve-T2I-5FcT.cjs} +3 -3
  110. package/dist/{resolve-ApSUK_HB.cjs.map → resolve-T2I-5FcT.cjs.map} +1 -1
  111. package/dist/{resolve-DY9TaVeV.mjs → resolve-r7PAs6ng.mjs} +3 -3
  112. package/dist/{resolve-DY9TaVeV.mjs.map → resolve-r7PAs6ng.mjs.map} +1 -1
  113. package/dist/{rollback-DXU6cr7q.mjs → rollback-BSdxnW3X.mjs} +5 -5
  114. package/dist/{rollback-DXU6cr7q.mjs.map → rollback-BSdxnW3X.mjs.map} +1 -1
  115. package/dist/{rollback-CdGhCk1d.cjs → rollback-CPSDjGse.cjs} +5 -5
  116. package/dist/{rollback-CdGhCk1d.cjs.map → rollback-CPSDjGse.cjs.map} +1 -1
  117. package/dist/{runtime-ekxfPNM-.d.mts → runtime-CNu2C2hB.d.mts} +2 -2
  118. package/dist/{runtime-D6A-C6zh.d.cts.map → runtime-CNu2C2hB.d.mts.map} +1 -1
  119. package/dist/{runtime-D6A-C6zh.d.cts → runtime-DMLgL26v.d.cts} +2 -2
  120. package/dist/{runtime-ekxfPNM-.d.mts.map → runtime-DMLgL26v.d.cts.map} +1 -1
  121. package/dist/{seeds-DbyqaXW4.mjs → seeds-BK8caVMm.mjs} +1707 -1612
  122. package/dist/seeds-BK8caVMm.mjs.map +1 -0
  123. package/dist/{seeds-BLzrSwEr.cjs → seeds-DQ0FG57Z.cjs} +2558 -2421
  124. package/dist/seeds-DQ0FG57Z.cjs.map +1 -0
  125. package/dist/{sniffer-ByWtH28i.mjs → sniffer-CqE_KTTp.mjs} +6 -6
  126. package/dist/{sniffer-ByWtH28i.mjs.map → sniffer-CqE_KTTp.mjs.map} +1 -1
  127. package/dist/{sniffer-BjG-1mlD.cjs → sniffer-hSf5mRgV.cjs} +6 -6
  128. package/dist/{sniffer-BjG-1mlD.cjs.map → sniffer-hSf5mRgV.cjs.map} +1 -1
  129. package/dist/stages-CS1sjcRb.cjs +174 -0
  130. package/dist/stages-CS1sjcRb.cjs.map +1 -0
  131. package/dist/stages-CSXt7N2b.mjs +174 -0
  132. package/dist/stages-CSXt7N2b.mjs.map +1 -0
  133. package/dist/target/public.cjs +1 -1
  134. package/dist/target/public.d.cts +2 -2
  135. package/dist/target/public.d.mts +2 -2
  136. package/dist/target/public.mjs +1 -1
  137. package/dist/{types-DZtj9jeI.d.cts → types-BZn-8ssD.d.mts} +75 -22
  138. package/dist/types-BZn-8ssD.d.mts.map +1 -0
  139. package/dist/{types-BzCavjGt.d.mts → types-DiZj6hqp.d.cts} +76 -21
  140. package/dist/types-DiZj6hqp.d.cts.map +1 -0
  141. package/dist/vitest.cjs +1 -1
  142. package/dist/vitest.mjs +1 -1
  143. package/dist/workspace/index.cjs +2 -1
  144. package/dist/workspace/index.d.cts +3 -3
  145. package/dist/workspace/index.d.mts +3 -3
  146. package/dist/workspace/index.mjs +2 -2
  147. package/dist/{workspace-Bp8FEkF-.cjs → workspace-Bic_R3tz.cjs} +345 -99
  148. package/dist/workspace-Bic_R3tz.cjs.map +1 -0
  149. package/dist/{workspace-BBdqmkch.mjs → workspace-Drz4jsvy.mjs} +314 -98
  150. package/dist/workspace-Drz4jsvy.mjs.map +1 -0
  151. package/package.json +15 -15
  152. package/dist/EndpointGenerator-DtnIGafh.mjs.map +0 -1
  153. package/dist/EndpointGenerator-cKlnllIu.cjs.map +0 -1
  154. package/dist/compose-D4sD4Lgr.mjs.map +0 -1
  155. package/dist/compose-qnDyPJNp.cjs.map +0 -1
  156. package/dist/config-L_6NivCp.d.mts.map +0 -1
  157. package/dist/config-Mr4rcXFC.d.cts.map +0 -1
  158. package/dist/deploy-C00DAKOt.mjs.map +0 -1
  159. package/dist/deploy-CXP2fXvK.cjs.map +0 -1
  160. package/dist/engine-BS3iX9Mu.cjs.map +0 -1
  161. package/dist/engine-CgzgIg-_.mjs.map +0 -1
  162. package/dist/index-C0UmP_lg.d.mts.map +0 -1
  163. package/dist/index-DTEPwLml.d.cts.map +0 -1
  164. package/dist/seeds-BLzrSwEr.cjs.map +0 -1
  165. package/dist/seeds-DbyqaXW4.mjs.map +0 -1
  166. package/dist/types-BzCavjGt.d.mts.map +0 -1
  167. package/dist/types-DZtj9jeI.d.cts.map +0 -1
  168. package/dist/workspace-BBdqmkch.mjs.map +0 -1
  169. package/dist/workspace-Bp8FEkF-.cjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -4,22 +4,22 @@ import { r as DEFAULT_TIMEOUT_MS, t as CommandFailed } from "./run-BJLIerMM.mjs"
4
4
  import { c as getDokployCredentials, f as removeDokployCredentials, i as LocalSandbox, l as getDokployRegistryId, m as storeHostingerToken, p as storeDokployCredentials, s as getCredentialsPath, u as getDokployToken } from "./credentials-pAtB70k3.mjs";
5
5
  import { l as withOwningTsconfigJsx, r as discover, u as ConstructGenerator } from "./discover-BPelzeGW.mjs";
6
6
  import { t as appKey } from "./derive-B4vDpzgP.mjs";
7
- import { A as deployedProblems, D as InvalidStages, I as resolveLogs, O as TEST_STAGE, c as getAppGkmConfig, j as stageProblems, n as allConstructGlobs, s as getAppBuildOrder } from "./workspace-BBdqmkch.mjs";
7
+ import { I as stageSampleRate, M as deployedProblems, N as stageProblems, O as InvalidStages, c as getAppBuildOrder, k as TEST_STAGE, l as getAppGkmConfig, r as allConstructGlobs } from "./workspace-Drz4jsvy.mjs";
8
8
  import { ConfigNotFound, NotABackendApp, NotInAnApp, loadAppConfig, loadConfig, loadWorkspaceConfig, loadWorkspaceSettings, parseModuleConfig } from "./config.mjs";
9
- import { B as WorkerGenerator, Ct as postgresDatabaseNames, F as parseDevServices, Fn as withCustomSecret, G as FAKE_ENV, Gt as imageBuildCredentials, H as workerEntryDir, J as derivedContainers, Jt as eventsBackendFor, K as backendsOf$1, Kt as isImageBuild, Mn as maskPassword, Pn as toEmbeddableSecrets, Tt as quoteIdentifier, U as QueueGenerator, V as workerBundleName, W as CronGenerator, Y as reconcileWorkspace, Yt as providerOf, Zt as CLUSTER_DATABASE, _t as LOCAL_CA_PATH, an as primaryPortKey, cn as findAvailablePort, dn as loadPortState, et as readLocalCredentials, fn as loadSecretsForApp, hn as secretsStoreFor, in as portsOf, kt as pgClient, ln as isPortAvailable, nn as fakeKey, on as createCredentialsPreload, pn as prepareEntryCredentials, q as constructGlobs, qt as cacheBackendFor, sn as createEntryWrapper, tn as describeServices, un as loadEnvFiles, vn as FileSecretsStore, vt as forgetState, wn as deployIdentity, x as dockerCommand, xn as projectKey, yn as getKeyPath, z as workerUnits } from "./seeds-DbyqaXW4.mjs";
10
- import { a as parseTelemetryFlag, i as telemetryFor, n as runtimeFor, t as EndpointGenerator } from "./EndpointGenerator-DtnIGafh.mjs";
9
+ import { $t as describeServices, An as maskPassword, B as WorkerGenerator, F as parseDevServices, Ft as cacheBackendFor, G as FAKE_ENV, H as workerEntryDir, Ht as TELEMETRY_CONTAINER, It as eventsBackendFor, J as derivedContainers, Jt as scopeTelemetryEnv, K as backendsOf$1, Kt as appTelemetry, Lt as providerOf, Mn as toEmbeddableSecrets, Nn as withCustomSecret, Nt as imageBuildCredentials, Pn as LOCAL_LOGS_EMAIL, Pt as isImageBuild, Sn as deployIdentity, U as QueueGenerator, V as workerBundleName, W as CronGenerator, Xt as telemetryOf, Y as reconcileWorkspace, Yt as surfaceClientTraced, _n as getKeyPath, an as createEntryWrapper, bt as pgClient, cn as loadEnvFiles, ct as LOCAL_CA_PATH, dn as prepareEntryCredentials, en as fakeKey, et as readLocalCredentials, gn as FileSecretsStore, gt as quoteIdentifier, in as createCredentialsPreload, ln as loadPortState, lt as forgetState, mt as postgresDatabaseNames, nn as portsOf, on as findAvailablePort, pn as secretsStoreFor, q as constructGlobs, rn as primaryPortKey, sn as isPortAvailable, un as loadSecretsForApp, x as dockerCommand, yn as projectKey, z as workerUnits, zt as CLUSTER_DATABASE } from "./seeds-BK8caVMm.mjs";
10
+ import { c as startTelemetryCode, l as telemetryFor, n as runtimeFor, o as mountRequestSpansCode, r as parseTelemetryFlag, s as routeTelemetry, t as EndpointGenerator, u as writeTelemetryModule } from "./EndpointGenerator-CVc6Oi1Y.mjs";
11
11
  import { generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi.mjs";
12
- import { s as isCacheBackend, t as SubscriberGenerator } from "./SubscriberGenerator-DQZeGkDW.mjs";
12
+ import { s as isCacheBackend, t as SubscriberGenerator } from "./SubscriberGenerator-BPqQf8fB.mjs";
13
13
  import { a as describeHolder, i as assignAppPorts, o as holderOf, r as appTag, s as withAppPorts, t as APP_TAG_ENV } from "./appPorts-C9z9Pzn9.mjs";
14
- import { n as driversFor, r as eventsBackendsIn, t as cacheBackendsIn } from "./drivers-B4RP2B2O.mjs";
14
+ import { n as driversFor, r as eventsBackendsIn, t as cacheBackendsIn } from "./drivers-DTZzxeIl.mjs";
15
15
  import { n as withCompute, r as withRoutes, t as manifestModule } from "./emit-C1e8qD08.mjs";
16
- import { g as createStateStore, n as sniffAppEnvironment, o as DokployApi, x as keyFingerprint } from "./sniffer-ByWtH28i.mjs";
17
- import { t as deploy$1 } from "./deploy-C00DAKOt.mjs";
18
- import { a as stackOverrideFile, c as parseDigests, d as removeEdgeRoutes, f as EDGE_PROJECT, i as ComposePushNeedsBuild, l as pinnedRef, o as composeProject, p as dockerCompose, r as ComposePinNeedsPull, s as stackDir, t as composeTarget, u as edgeDir } from "./compose-D4sD4Lgr.mjs";
16
+ import { g as createStateStore, n as sniffAppEnvironment, o as DokployApi, x as keyFingerprint } from "./sniffer-CqE_KTTp.mjs";
17
+ import { t as deploy$1 } from "./deploy-BwIKGxWH.mjs";
18
+ import { a as stackOverrideFile, c as parseDigests, d as removeEdgeRoutes, f as EDGE_PROJECT, i as ComposePushNeedsBuild, l as pinnedRef, o as composeProject, p as dockerCompose, r as ComposePinNeedsPull, s as stackDir, t as composeTarget, u as edgeDir } from "./compose-C5wz8mf9.mjs";
19
19
  import { o as loadCredentialSchemas, t as CredentialsInvalid } from "./credentialSchemas-BJqRJNK4.mjs";
20
- import { t as LocalStateStore } from "./LocalStateStore-C72bwpG2.mjs";
20
+ import { t as LocalStateStore } from "./LocalStateStore-D1VfxRuK.mjs";
21
21
  import { a as getExecCommand, c as validateProjectName, i as findWorkspaceRoot, n as detectPackageManager$1, o as getInstallCommand, r as findWorkspacePackages, s as getRunCommand, t as checkDirectoryExists } from "./utils-D_fxUqvU.mjs";
22
- import { a as writeTestHarness, i as TEST_MANIFEST_ENV, n as TEST_READY_FILE, t as TEST_READY_ENV } from "./ready-DhZbZtFw.mjs";
22
+ import { a as writeTestHarness, i as TEST_MANIFEST_ENV, n as TEST_READY_FILE, t as TEST_READY_ENV } from "./ready-b6vm0-W-.mjs";
23
23
  import { createRequire } from "node:module";
24
24
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
25
25
  import { Command, Option } from "commander";
@@ -47,7 +47,7 @@ import crypto from "crypto";
47
47
  import { Function } from "@geekmidas/constructs/functions";
48
48
  import { Topic } from "@geekmidas/constructs/topic";
49
49
  //#region package.json
50
- var version = "10.0.0-alpha.82";
50
+ var version = "10.0.0-alpha.84";
51
51
  //#endregion
52
52
  //#region src/prompt.ts
53
53
  /**
@@ -130,7 +130,7 @@ const logger$8 = console;
130
130
  * Validate Dokploy token by making a test API call
131
131
  */
132
132
  async function validateDokployToken(endpoint, token) {
133
- const { DokployApi } = await import("./sniffer-ByWtH28i.mjs").then((n) => n.c);
133
+ const { DokployApi } = await import("./sniffer-CqE_KTTp.mjs").then((n) => n.c);
134
134
  return new DokployApi({
135
135
  baseUrl: endpoint,
136
136
  token
@@ -530,10 +530,9 @@ var NoLocalCredentials = class extends Error {
530
530
  /** The logins of every service the workspace last published. */
531
531
  function devCredentials(workspace, credentials, ports) {
532
532
  const containers = SIGNED_IN.filter((container) => portsOf(container).some((port) => ports[port.key] !== void 0));
533
- const logs = resolveLogs(workspace.deploy?.compose?.logs);
534
- const local = logs ? {
535
- url: logs.public ? `https://logs.${workspace.name}.localhost` : `http://localhost:${logs.port}`,
536
- via: `gkm compose --stage ${workspace.stages.local}`
533
+ const local = ports.openobserve !== void 0 ? {
534
+ url: `http://localhost:${ports.openobserve}`,
535
+ via: "gkm dev"
537
536
  } : void 0;
538
537
  return {
539
538
  workspace: workspace.name,
@@ -753,6 +752,7 @@ async function collect(records) {
753
752
  root,
754
753
  stage: lead.workspace.stage,
755
754
  manifest: group.find((r) => r.manifest)?.manifest ?? null,
755
+ telemetry: group.find((r) => r.telemetry)?.telemetry ?? null,
756
756
  apps: discovered,
757
757
  allowedOrigins: [...new Set(group.flatMap((r) => r.allowedOrigins))]
758
758
  });
@@ -1097,6 +1097,7 @@ var DiscoverySession = class DiscoverySession {
1097
1097
  workspace: options.workspace,
1098
1098
  allowedOrigins: [...options.allowedOrigins ?? []],
1099
1099
  ...options.manifest ? { manifest: options.manifest } : {},
1100
+ ...options.telemetry ? { telemetry: options.telemetry } : {},
1100
1101
  apps: options.apps
1101
1102
  };
1102
1103
  writeRecord(registry, record);
@@ -1850,7 +1851,8 @@ async function devCommand(options) {
1850
1851
  hooks,
1851
1852
  skipBundle: true,
1852
1853
  bustCache,
1853
- serveEmpty: true
1854
+ serveEmpty: true,
1855
+ workspaceName: workspace.name
1854
1856
  }));
1855
1857
  logger$5.log(`🔨 Building ${workspaceAppName ?? "the app"}`);
1856
1858
  const initial = await build();
@@ -1858,6 +1860,7 @@ async function devCommand(options) {
1858
1860
  let secretsJsonPath;
1859
1861
  let publicUrl;
1860
1862
  let manifest;
1863
+ let telemetryUi;
1861
1864
  const appSecrets = await loadSecretsForApp(workspace ? await secretsStoreFor(workspace, config.stages.local) : new FileSecretsStore(secretsRoot), config.stages.local);
1862
1865
  if (workspace) {
1863
1866
  const reconciled = await reconcileWorkspace(workspace, {
@@ -1869,10 +1872,25 @@ async function devCommand(options) {
1869
1872
  logger$5.log("🐳 Services");
1870
1873
  for (const line of describeServices(reconciled.services)) logger$5.log(line);
1871
1874
  }
1875
+ if (!process.env["GKM_DISCOVERY_QUIET"] && reconciled.credentials && reconciled.ports) {
1876
+ const { services: logins } = devCredentials(workspace, reconciled.credentials, reconciled.ports);
1877
+ if (logins.length > 0) {
1878
+ logger$5.log("🔑 Logins (again any time: gkm dev:credentials)");
1879
+ for (const line of describeLogins(logins)) logger$5.log(line);
1880
+ }
1881
+ }
1872
1882
  Object.assign(appSecrets, reconciled.env);
1883
+ const scoped = scopeTelemetryEnv(appSecrets, {
1884
+ uses: reconciled.manifest !== void 0 && appTelemetry(reconciled.manifest, workspaceAppName) !== void 0,
1885
+ serviceName: workspaceAppName,
1886
+ telemetry: reconciled.env
1887
+ });
1888
+ for (const key of Object.keys(appSecrets)) if (!(key in scoped)) delete appSecrets[key];
1889
+ Object.assign(appSecrets, scoped);
1873
1890
  const own = reconciled.plan.resources.find((r) => (r.kind === "rest-api" || r.kind === "site") && appKey(r.id) === workspaceAppName);
1874
1891
  publicUrl = own ? reconciled.env[own.envKey] : void 0;
1875
1892
  manifest = reconciled.manifest;
1893
+ telemetryUi = telemetryUiOf(reconciled.services);
1876
1894
  }
1877
1895
  if (Object.keys(appSecrets).length > 0) {
1878
1896
  const secretsDir = join(secretsRoot, ".gkm");
@@ -1895,6 +1913,7 @@ async function devCommand(options) {
1895
1913
  },
1896
1914
  allowedOrigins: workspace.dev?.allowedOrigins ?? [],
1897
1915
  ...manifest ? { manifest } : {},
1916
+ ...telemetryUi ? { telemetry: telemetryUi } : {},
1898
1917
  apps: [{
1899
1918
  name: workspaceAppName,
1900
1919
  type: "backend",
@@ -2203,17 +2222,20 @@ async function workspaceDevCommand(configured, options) {
2203
2222
  logger$5.log("🔑 Logins (again any time: gkm dev:credentials)");
2204
2223
  for (const line of describeLogins(logins)) logger$5.log(line);
2205
2224
  }
2206
- const secretsEnv = {
2225
+ const secretsEnv = scopeTelemetryEnv({
2207
2226
  ...rawSecrets,
2208
2227
  ...reconciled.env
2209
- };
2228
+ }, {
2229
+ uses: false,
2230
+ serviceName: workspace.name
2231
+ });
2210
2232
  const fakes = options.fake ? await serveFakes(reconciled.fakes, reconciled.ports) : [];
2211
2233
  for (const { id, port } of fakes) logger$5.log(`🎭 ${id} fake: http://localhost:${port}`);
2212
2234
  logger$5.log(options.migrate || options.seed ? "🗄️ Migrating the databases" : "🗄️ Checking for pending migrations");
2213
2235
  if (options.migrate || options.seed) {
2214
- const { prepareDevDatabases } = await import("./migrate-CCejlgU0.mjs");
2236
+ const { prepareDevDatabases } = await import("./migrate-CdXcX0XR.mjs");
2215
2237
  await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
2216
- } else await import("./migrate-CCejlgU0.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
2238
+ } else await import("./migrate-CdXcX0XR.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
2217
2239
  logger$5.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
2218
2240
  });
2219
2241
  const appUrls = {};
@@ -2268,6 +2290,7 @@ async function workspaceDevCommand(configured, options) {
2268
2290
  break;
2269
2291
  }
2270
2292
  }
2293
+ const workspaceTelemetry = telemetryUiOf(reconciled.services);
2271
2294
  const discovery = await joinDiscovery({
2272
2295
  role: "workspace",
2273
2296
  port: discoveryPort(workspace.dev),
@@ -2278,6 +2301,7 @@ async function workspaceDevCommand(configured, options) {
2278
2301
  },
2279
2302
  allowedOrigins: workspace.dev?.allowedOrigins ?? [],
2280
2303
  ...reconciled.manifest ? { manifest: reconciled.manifest } : {},
2304
+ ...workspaceTelemetry ? { telemetry: workspaceTelemetry } : {},
2281
2305
  apps: buildOrder.flatMap((name) => {
2282
2306
  const app = workspace.apps[name];
2283
2307
  if (!app) return [];
@@ -2475,6 +2499,18 @@ var EntryRunner = class {
2475
2499
  }
2476
2500
  };
2477
2501
  /**
2502
+ * Where the local OpenObserve is, and who signs in to it, when reconcile runs
2503
+ * one — for the discovery endpoint, which is told no password.
2504
+ */
2505
+ function telemetryUiOf(services) {
2506
+ const ui = services.find((s) => s.container === TELEMETRY_CONTAINER);
2507
+ if (!ui) return void 0;
2508
+ return {
2509
+ url: ui.address,
2510
+ email: LOCAL_LOGS_EMAIL
2511
+ };
2512
+ }
2513
+ /**
2478
2514
  * Generate the content of the dev server entry file (server.ts).
2479
2515
  * Uses dynamic import for createApp so Credentials are populated
2480
2516
  * before any app modules evaluate.
@@ -2520,6 +2556,7 @@ if (existsSync(secretsPath)) {
2520
2556
  injectWs(server);
2521
2557
  console.log('🔌 Telescope real-time updates enabled');
2522
2558
  }`;
2559
+ const telemetry = startTelemetryCode(["/health", "/ready"]);
2523
2560
  if (selfServing) return `#!/usr/bin/env node
2524
2561
  /**
2525
2562
  * Development server entry point for a surface that serves itself
@@ -2529,8 +2566,17 @@ ${credentialsInjection}${parentWatch}const port = process.argv.includes('--port'
2529
2566
  ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
2530
2567
  : 3000;
2531
2568
 
2569
+ ${telemetry}
2532
2570
  // Dynamic import so Credentials are populated before the surface evaluates
2533
- const { app } = await import('${appImportPath}');
2571
+ const { app: surface } = await import('${appImportPath}');
2572
+
2573
+ // Behind the request spans when there are any, as the production server is.
2574
+ const { Hono } = await import('hono');
2575
+ const app = requestSpans ? new Hono() : surface;
2576
+ if (requestSpans) {
2577
+ ${mountRequestSpansCode("app").trim().replace(/\n/g, "\n ")}
2578
+ app.route('/', surface);
2579
+ }
2534
2580
 
2535
2581
  ${serveCode.replace(/^ {4}/gm, "")}
2536
2582
  `;
@@ -2543,11 +2589,16 @@ ${credentialsInjection}${parentWatch}const port = process.argv.includes('--port'
2543
2589
  ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
2544
2590
  : 3000;
2545
2591
 
2592
+ ${telemetry}
2546
2593
  // Dynamic import so Credentials are populated before env.ts evaluates
2547
2594
  const { createApp } = await import('${appImportPath}');
2548
2595
 
2596
+ // The span middleware first, as the production server mounts it.
2597
+ const { Hono } = await import('hono');
2598
+ const root = new Hono();
2599
+ ${mountRequestSpansCode("root")}
2549
2600
  // createApp is async to support optional WebSocket setup
2550
- const { app, start } = await createApp(undefined, ${enableOpenApi});
2601
+ const { app, start } = await createApp(root, ${enableOpenApi});
2551
2602
 
2552
2603
  // Start the server
2553
2604
  start({
@@ -2989,6 +3040,7 @@ export default app;
2989
3040
  */
2990
3041
  async function writeSurfaceServer(outputDir, options) {
2991
3042
  await mkdir(outputDir, { recursive: true });
3043
+ await writeTelemetryModule(outputDir, options.telemetry);
2992
3044
  const shutdown = options.gracefulShutdown ? `
2993
3045
  // Stop taking requests, let in-flight ones finish, then close what the
2994
3046
  // construct opened — bounded by GKM_SHUTDOWN_TIMEOUT_MS, under Docker's 10s
@@ -3015,12 +3067,14 @@ process.on('SIGINT', shutdown);
3015
3067
  */
3016
3068
  import { serve } from '@hono/node-server';
3017
3069
  import { Hono } from 'hono';
3018
- ${options.gracefulShutdown ? "import { runShutdownHooks } from '@geekmidas/constructs';\n" : ""}import { app as surface } from './app.js';
3070
+ ${options.gracefulShutdown ? "import { runShutdownHooks } from '@geekmidas/constructs';\n" : ""}
3071
+ ${startTelemetryCode([options.healthCheck])}
3072
+ const { app: surface } = await import('./app.js');
3019
3073
 
3020
3074
  const port = Number(process.env.PORT) || 3000;
3021
3075
 
3022
3076
  const app = new Hono();
3023
- // First, so a probe is answered without reaching the surface's own routes.
3077
+ ${mountRequestSpansCode("app")}// First, so a probe is answered without reaching the surface's own routes.
3024
3078
  app.get(${JSON.stringify(options.healthCheck)}, (c) =>
3025
3079
  c.json({ status: 'ok', timestamp: Date.now() }),
3026
3080
  );
@@ -3217,11 +3271,23 @@ async function buildApp(input) {
3217
3271
  });
3218
3272
  if (selfServing) {
3219
3273
  await writeSurfaceEntry(join(appRoot, ".gkm", "server"), selfServing.source);
3274
+ const telemetry = telemetryFor({
3275
+ node: telemetryOf(declared, selfServing.id),
3276
+ appRoot,
3277
+ app: appNameOf(input),
3278
+ cwd: workspaceRoot,
3279
+ serviceName: selfServing.id,
3280
+ ...input.workspaceName && { workspaceName: input.workspaceName }
3281
+ });
3282
+ await writeTelemetryModule(join(appRoot, ".gkm", "server"), telemetry);
3220
3283
  logger$4.log(`Generated a server for ${selfServing.id} from its own declaration`);
3221
3284
  if (production?.bundle && !input.skipBundle) {
3222
3285
  const outputDir = join(appRoot, ".gkm", "server");
3223
- const entryPoint = await writeSurfaceServer(outputDir, production);
3224
- const { bundleServer } = await import("./bundler-DlKdxCGh.mjs");
3286
+ const entryPoint = await writeSurfaceServer(outputDir, {
3287
+ ...production,
3288
+ ...telemetry ? { telemetry } : {}
3289
+ });
3290
+ const { bundleServer } = await import("./bundler-D4030Afw.mjs");
3225
3291
  await bundleServer({
3226
3292
  entryPoint,
3227
3293
  outputDir: join(outputDir, "dist"),
@@ -3256,12 +3322,7 @@ async function buildApp(input) {
3256
3322
  events: eventsBackends
3257
3323
  }),
3258
3324
  eventsBackends,
3259
- markOptional: input.markOptional ?? false,
3260
- ...production && { telemetry: telemetryFor({
3261
- appRoot,
3262
- surfaceId: derived.surface?.id,
3263
- workspaceName: input.workspaceName
3264
- }) }
3325
+ markOptional: input.markOptional ?? false
3265
3326
  };
3266
3327
  const endpointGenerator = new EndpointGenerator();
3267
3328
  const functionGenerator = new FunctionGenerator();
@@ -3279,6 +3340,15 @@ async function buildApp(input) {
3279
3340
  topicGenerator.load(code, appRoot, bustCache)
3280
3341
  ]);
3281
3342
  const allEndpoints = servedBy(loadedEndpoints, derived.surface);
3343
+ if (target === "server") buildContext.telemetry = telemetryFor({
3344
+ node: derived.surface ? telemetryOf(declared, derived.surface.id) : void 0,
3345
+ appRoot,
3346
+ app: appNameOf(input),
3347
+ cwd: workspaceRoot,
3348
+ serviceName: derived.surface?.id ?? basename(appRoot),
3349
+ ...input.workspaceName && { workspaceName: input.workspaceName },
3350
+ routes: routeTelemetry(allEndpoints.map(({ construct }) => construct))
3351
+ });
3282
3352
  logger$4.log(`Found ${allEndpoints.length} endpoints`);
3283
3353
  logger$4.log(`Found ${allFunctions.length} functions`);
3284
3354
  logger$4.log(`Found ${allCrons.length} crons`);
@@ -3306,6 +3376,14 @@ async function buildApp(input) {
3306
3376
  const appName = Object.entries(input.workspace.apps).find(([, app]) => resolve(input.workspace.root, app.path) === resolve(appRoot))?.[0];
3307
3377
  await buildWorkers({
3308
3378
  context: buildContext,
3379
+ telemetry: (workerId) => telemetryFor({
3380
+ node: telemetryOf(declared, workerId),
3381
+ appRoot,
3382
+ app: appNameOf(input),
3383
+ cwd: workspaceRoot,
3384
+ serviceName: workerId,
3385
+ ...input.workspaceName && { workspaceName: input.workspaceName }
3386
+ }),
3309
3387
  workers: workerUnits(input.workspace, declared, background).filter((unit) => unit.app === appName),
3310
3388
  serverDir: join(appRoot, ".gkm", "server"),
3311
3389
  crons: allCrons,
@@ -3317,7 +3395,14 @@ async function buildApp(input) {
3317
3395
  }
3318
3396
  await generateOpenApi(allEndpoints.map(({ construct }) => construct), {
3319
3397
  openapi: config.openapi,
3320
- root: workspaceRoot
3398
+ root: workspaceRoot,
3399
+ ...derived.surface ? clientTelemetry({
3400
+ manifest: declared,
3401
+ surfaceId: derived.surface.id,
3402
+ telemetry: input.workspace?.deploy?.telemetry,
3403
+ stage: input.stage,
3404
+ localStage: input.workspace?.stages.local ?? config.stages?.local
3405
+ }) : {}
3321
3406
  });
3322
3407
  return {
3323
3408
  ...result,
@@ -3335,9 +3420,11 @@ async function buildWorkers(input) {
3335
3420
  const crons = owned(input.crons);
3336
3421
  const queues = owned(input.queues);
3337
3422
  const subscribers = owned(input.subscribers);
3423
+ const telemetry = input.telemetry(worker.id);
3338
3424
  const entryPoint = await generator.build({
3339
3425
  context: input.context,
3340
3426
  workerId: worker.id,
3427
+ ...telemetry ? { telemetry } : {},
3341
3428
  outputDir: workerEntryDir(input.serverDir, worker.id),
3342
3429
  crons,
3343
3430
  queues,
@@ -3345,7 +3432,7 @@ async function buildWorkers(input) {
3345
3432
  });
3346
3433
  logger$4.log(`Generated worker ${worker.id}: ${crons.length} crons, ${queues.length} queues, ${subscribers.length} subscribers`);
3347
3434
  if (!input.bundle) continue;
3348
- const { bundleServer } = await import("./bundler-DlKdxCGh.mjs");
3435
+ const { bundleServer } = await import("./bundler-D4030Afw.mjs");
3349
3436
  const outfile = workerBundleName(worker.id);
3350
3437
  await bundleServer({
3351
3438
  entryPoint,
@@ -3364,6 +3451,22 @@ async function buildWorkers(input) {
3364
3451
  logger$4.log(`✅ Bundle complete: .gkm/server/dist/${outfile}`);
3365
3452
  }
3366
3453
  }
3454
+ /**
3455
+ * Whether the surface's generated client propagates trace context, and at
3456
+ * what rate: on when a site that calls it has an edge to a `Telemetry`
3457
+ * construct, at the stage's sample rate — every trace locally, and on a build
3458
+ * for no stage. Off for a stage that sends no telemetry.
3459
+ */
3460
+ function clientTelemetry(options) {
3461
+ if (!surfaceClientTraced(options.manifest, options.surfaceId)) return {};
3462
+ const local = !options.stage || options.stage === options.localStage;
3463
+ const sampleRate = stageSampleRate(options.telemetry, options.stage ?? "", local);
3464
+ return sampleRate === void 0 ? {} : { telemetry: { sampleRate } };
3465
+ }
3466
+ /** The app being built, by its workspace name or its directory's. */
3467
+ function appNameOf(input) {
3468
+ return (input.workspace ? Object.entries(input.workspace.apps).find(([, app]) => resolve(input.workspace.root, app.path) === resolve(input.appRoot))?.[0] : void 0) ?? basename(input.appRoot);
3469
+ }
3367
3470
  async function buildForTarget(target, context, appRoot, workspaceRoot, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, queueGenerator, topicGenerator, endpoints, functions, crons, subscribers, queues, topics, enableOpenApi, skipBundle, stage) {
3368
3471
  const outputDir = join(appRoot, ".gkm", target);
3369
3472
  await Promise.all([...SUPERSEDED_OUTPUT.map((dir) => join(appRoot, ".gkm", dir)), ...appRoot === workspaceRoot ? [] : [join(appRoot, ".gkm", "manifest")]].map((dir) => rm(dir, {
@@ -3415,7 +3518,7 @@ async function buildForTarget(target, context, appRoot, workspaceRoot, endpointG
3415
3518
  let masterKey;
3416
3519
  if (context.production?.bundle && !skipBundle) {
3417
3520
  logger$4.log(`\n📦 Bundling production server...`);
3418
- const { bundleServer } = await import("./bundler-DlKdxCGh.mjs");
3521
+ const { bundleServer } = await import("./bundler-D4030Afw.mjs");
3419
3522
  const allConstructs = [
3420
3523
  ...endpoints.map((e) => e.construct),
3421
3524
  ...functions.map((f) => f.construct),
@@ -5102,12 +5205,12 @@ const PNPM_VERSION = "pnpm@10.13.1";
5102
5205
  const BIOME_SCHEMA = `https://biomejs.dev/schemas/${DEPENDENCY_VERSIONS["@biomejs/biome"].replace(/^[~^]/, "")}/schema.json`;
5103
5206
  //#endregion
5104
5207
  //#region src/init/versions.ts
5105
- const require$1 = createRequire(import.meta.url);
5208
+ const require$2 = createRequire(import.meta.url);
5106
5209
  function loadPackageJson() {
5107
5210
  try {
5108
- return require$1("../package.json");
5211
+ return require$2("../package.json");
5109
5212
  } catch {
5110
- return require$1("../../package.json");
5213
+ return require$2("../../package.json");
5111
5214
  }
5112
5215
  }
5113
5216
  /**
@@ -5117,25 +5220,25 @@ function loadPackageJson() {
5117
5220
  * Run: pnpm --filter @geekmidas/cli sync-versions
5118
5221
  */
5119
5222
  const GEEKMIDAS_VERSIONS = {
5120
- "@geekmidas/audit": "~10.0.0-alpha.82",
5121
- "@geekmidas/auth": "~10.0.0-alpha.82",
5122
- "@geekmidas/cache": "~10.0.0-alpha.82",
5123
- "@geekmidas/client": "~10.0.0-alpha.82",
5124
- "@geekmidas/cloud": "~10.0.0-alpha.82",
5125
- "@geekmidas/constructs": "~10.0.0-alpha.82",
5126
- "@geekmidas/db": "~10.0.0-alpha.82",
5127
- "@geekmidas/emailkit": "~10.0.0-alpha.82",
5128
- "@geekmidas/envkit": "~10.0.0-alpha.82",
5129
- "@geekmidas/errors": "~10.0.0-alpha.82",
5130
- "@geekmidas/events": "~10.0.0-alpha.82",
5131
- "@geekmidas/logger": "~10.0.0-alpha.82",
5132
- "@geekmidas/manifest": "~10.0.0-alpha.82",
5133
- "@geekmidas/rate-limit": "~10.0.0-alpha.82",
5134
- "@geekmidas/schema": "~10.0.0-alpha.82",
5135
- "@geekmidas/services": "~10.0.0-alpha.82",
5136
- "@geekmidas/storage": "~10.0.0-alpha.82",
5137
- "@geekmidas/telescope": "~10.0.0-alpha.82",
5138
- "@geekmidas/testkit": "~10.0.0-alpha.82",
5223
+ "@geekmidas/audit": "~10.0.0-alpha.84",
5224
+ "@geekmidas/auth": "~10.0.0-alpha.84",
5225
+ "@geekmidas/cache": "~10.0.0-alpha.84",
5226
+ "@geekmidas/client": "~10.0.0-alpha.84",
5227
+ "@geekmidas/cloud": "~10.0.0-alpha.84",
5228
+ "@geekmidas/constructs": "~10.0.0-alpha.84",
5229
+ "@geekmidas/db": "~10.0.0-alpha.84",
5230
+ "@geekmidas/emailkit": "~10.0.0-alpha.84",
5231
+ "@geekmidas/envkit": "~10.0.0-alpha.84",
5232
+ "@geekmidas/errors": "~10.0.0-alpha.84",
5233
+ "@geekmidas/events": "~10.0.0-alpha.84",
5234
+ "@geekmidas/logger": "~10.0.0-alpha.84",
5235
+ "@geekmidas/manifest": "~10.0.0-alpha.84",
5236
+ "@geekmidas/rate-limit": "~10.0.0-alpha.84",
5237
+ "@geekmidas/schema": "~10.0.0-alpha.84",
5238
+ "@geekmidas/services": "~10.0.0-alpha.84",
5239
+ "@geekmidas/storage": "~10.0.0-alpha.84",
5240
+ "@geekmidas/telescope": "~10.0.0-alpha.84",
5241
+ "@geekmidas/testkit": "~10.0.0-alpha.84",
5139
5242
  "@geekmidas/cli": `~${loadPackageJson().version}`
5140
5243
  };
5141
5244
  //#endregion
@@ -5508,6 +5611,38 @@ coverage/
5508
5611
  return files;
5509
5612
  }
5510
5613
  //#endregion
5614
+ //#region src/init/stagesAction.ts
5615
+ /**
5616
+ * How a scaffolded workflow names the `stages` action.
5617
+ *
5618
+ * A published CLI pins it to the commit it was released from: the release
5619
+ * build is given that commit as `GKM_RELEASE_COMMIT`, which the build writes
5620
+ * into the bundle (`define` in tsdown.config.ts). The action at that commit is
5621
+ * the one released beside this CLI, and a commit SHA cannot be moved under a
5622
+ * project the way a branch or a tag can.
5623
+ *
5624
+ * A CLI built anywhere else — a checkout, a local build — knows no released
5625
+ * commit, and names `main`.
5626
+ */
5627
+ const require$1 = createRequire(import.meta.url);
5628
+ function cliVersion() {
5629
+ try {
5630
+ return require$1("../package.json").version;
5631
+ } catch {
5632
+ return require$1("../../package.json").version;
5633
+ }
5634
+ }
5635
+ /** The action, as a workflow's `uses:` names it before the `@`. */
5636
+ const STAGES_ACTION = "geekmidas/toolbox/actions/stages";
5637
+ const COMMIT = /^[0-9a-f]{40}$/;
5638
+ /**
5639
+ * The `uses:` value for the stages action, with a comment saying which CLI
5640
+ * release the pinned commit is.
5641
+ */
5642
+ function stagesActionUses(commit = "1a6631b7e6dc71b7dd7b7afb8ca5b82225454826", version = cliVersion()) {
5643
+ return commit && COMMIT.test(commit) ? `${STAGES_ACTION}@${commit} # @geekmidas/cli ${version}` : `${STAGES_ACTION}@main # pin to a commit SHA: see the action's README`;
5644
+ }
5645
+ //#endregion
5511
5646
  //#region src/init/generators/github.ts
5512
5647
  /**
5513
5648
  * The GitHub workflows a scaffold ships with.
@@ -5517,11 +5652,14 @@ coverage/
5517
5652
  * release, labelled from their titles.
5518
5653
  * - `deploy.yml` — when a deploy target was picked: a push to main deploys the
5519
5654
  * stages that are not protected, publishing the drafted release deploys the
5520
- * protected ones.
5655
+ * protected ones, and a manual run deploys the one stage it names.
5521
5656
  *
5522
- * The deploy workflow names no stage. It reads `stages` from gkm.config.ts at
5523
- * run time, so the config stays the one place stages are declared and a
5524
- * workflow cannot fall out of step with it.
5657
+ * The deploy workflow names no stage. Its first job runs the
5658
+ * `geekmidas/toolbox/actions/stages` action, which asks the project's own
5659
+ * `gkm stages --github-output` which stages the run builds and deploys — so
5660
+ * the config stays the one place stages are declared, the rules for each event
5661
+ * are tested code in the CLI, and a workflow cannot fall out of step with
5662
+ * either.
5525
5663
  */
5526
5664
  /** How each package manager is set up, installs, and runs a script. */
5527
5665
  const TOOLING = {
@@ -5707,46 +5845,21 @@ template: |
5707
5845
  **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
5708
5846
  `;
5709
5847
  }
5710
- function deploy(options) {
5711
- const pm = tooling(options);
5712
- const sst = options.deployTarget === "sst";
5713
- const credentials = sst ? `
5714
- # An IAM role GitHub assumes through OIDC — no long-lived AWS keys. Set
5715
- # AWS_ROLE_ARN as a variable on each environment.
5716
- - uses: aws-actions/configure-aws-credentials@v4
5717
- with:
5718
- role-to-assume: \${{ vars.AWS_ROLE_ARN }}
5719
- aws-region: ${options.region}
5720
- ` : "";
5721
- const keyDir = `~/.gkm/keys/${projectKey({ name: options.name })}`;
5722
- const stageSecrets = sst ? credentials : `
5723
- # The stage's secrets are kept in the encrypted .gkm/secrets/<stage>.json,
5724
- # and each environment holds its key as GKM_SECRETS_KEY. .gkm/ is
5725
- # gitignored, so a checkout has the key but not the file: set
5726
- # secrets.store in gkm.config.ts to a store CI can reach before deploying
5727
- # from here.
5728
- # Owner-only, as the CLI writes its own keys. The stage reaches the shell
5729
- # as a variable, never pasted into the script: on a manual run it is
5730
- # whatever was typed.
5731
- - name: Stage secrets key
5732
- run: |
5733
- mkdir -p -m 700 ${keyDir}
5734
- (umask 077 && printf '%s' "$KEY" > ${keyDir}/"$STAGE".key)
5735
- env:
5736
- KEY: \${{ secrets.GKM_SECRETS_KEY }}
5737
- STAGE: \${{ matrix.stage }}
5738
- `;
5739
- const deployEnv = sst ? "" : `
5740
- DOKPLOY_API_TOKEN: \${{ secrets.DOKPLOY_API_TOKEN }}
5741
- DOKPLOY_ENDPOINT: \${{ vars.DOKPLOY_ENDPOINT }}`;
5848
+ /**
5849
+ * What every deploy workflow starts with: its triggers, and the `stages` job,
5850
+ * which asks the project's own gkm (through the stages action) which stages
5851
+ * this run builds and deploys. Nothing in a workflow names a stage.
5852
+ */
5853
+ function deployHeader(build) {
5742
5854
  return `name: Deploy
5743
5855
 
5744
5856
  # Which stages exist, and which are protected, is read from \`stages\` in
5745
- # gkm.config.ts — nothing here names one:
5857
+ # gkm.config.ts by the project's own gkm, through the stages action — nothing
5858
+ # here names one:
5746
5859
  #
5747
- # push to main every deployed stage that is not protected
5860
+ # push to main ${build ? "builds every deployed stage, deploys those not protected" : "every deployed stage that is not protected"}
5748
5861
  # release published the protected stages (publish the drafted release)
5749
- # run workflow the one stage you name
5862
+ # run workflow the one stage you name, at the ref you name
5750
5863
  on:
5751
5864
  push:
5752
5865
  branches: [main]
@@ -5757,17 +5870,27 @@ on:
5757
5870
  stage:
5758
5871
  description: A deployed stage from gkm.config.ts
5759
5872
  required: true
5873
+ type: string
5874
+ ref:
5875
+ description: The commit, tag or branch to deploy (default the latest on main)
5876
+ required: false
5877
+ type: string
5760
5878
 
5761
5879
  permissions:
5762
5880
  contents: read
5763
- id-token: write
5764
-
5765
- jobs:
5766
- stages:
5881
+ `;
5882
+ }
5883
+ function stagesJob(options) {
5884
+ const pm = tooling(options);
5885
+ return ` stages:
5767
5886
  name: Pick stages
5768
5887
  runs-on: ubuntu-latest
5769
5888
  outputs:
5770
- stages: \${{ steps.pick.outputs.stages }}
5889
+ build: \${{ steps.stages.outputs.build }}
5890
+ deploy: \${{ steps.stages.outputs.deploy }}
5891
+ has-build: \${{ steps.stages.outputs.has-build }}
5892
+ has-deploy: \${{ steps.stages.outputs.has-deploy }}
5893
+ aws-region: \${{ steps.stages.outputs.aws-region }}
5771
5894
  steps:
5772
5895
  - uses: actions/checkout@v4
5773
5896
 
@@ -5775,44 +5898,286 @@ ${pm.setup}
5775
5898
  - name: Install
5776
5899
  run: ${pm.install}
5777
5900
 
5901
+ # Runs \`gkm stages --github-output\`: the rules for each event are the
5902
+ # installed CLI's, and a manual run naming a stage gkm.config.ts does not
5903
+ # deploy fails here.
5778
5904
  - name: Read stages from gkm.config.ts
5779
- id: pick
5905
+ id: stages
5906
+ uses: ${stagesActionUses()}
5907
+ with:
5908
+ stage: \${{ inputs.stage }}
5909
+ `;
5910
+ }
5911
+ /**
5912
+ * The registry login a compose build pushes with: GitHub's own token on
5913
+ * ghcr.io, otherwise a username variable and a password secret.
5914
+ */
5915
+ function registryLogin(registry) {
5916
+ const host = registry.split("/")[0];
5917
+ const ghcr = host === "ghcr.io";
5918
+ return ` - uses: docker/login-action@v3
5919
+ with:
5920
+ registry: ${host}
5921
+ username: ${ghcr ? "${{ github.actor }}" : "${{ vars.REGISTRY_USERNAME }}"}
5922
+ password: ${ghcr ? "${{ secrets.GITHUB_TOKEN }}" : "${{ secrets.REGISTRY_PASSWORD }}"}
5923
+ `;
5924
+ }
5925
+ /**
5926
+ * The compose deploy: images are built and pushed by the runner, where the
5927
+ * code is, and pulled by the server, where the stage runs.
5928
+ *
5929
+ * - `build` (on a push): every deployed stage's images, tagged with the
5930
+ * commit, and their digests kept as the artifact `digests-<stage>`.
5931
+ * - `deploy`: the commit the event names — a release's tag, a manual run's
5932
+ * `ref` — then that commit's push build, whose digests pin each image. Over
5933
+ * SSH, with a pinned host key, the server checks the commit out and runs
5934
+ * `gkm compose` on exactly those images.
5935
+ */
5936
+ function composeDeploy(options) {
5937
+ const pm = tooling(options);
5938
+ const registry = options.registry ?? "";
5939
+ return `${deployHeader(true)}
5940
+ jobs:
5941
+ ${stagesJob(options)}
5942
+ build:
5943
+ name: Build \${{ matrix.stage }}
5944
+ needs: stages
5945
+ if: needs.stages.outputs.has-build == 'true'
5946
+ runs-on: ubuntu-latest
5947
+ strategy:
5948
+ fail-fast: false
5949
+ matrix:
5950
+ stage: \${{ fromJSON(needs.stages.outputs.build) }}
5951
+ # One GitHub environment per stage: its secrets, variables and approvals.
5952
+ environment: \${{ matrix.stage }}
5953
+ permissions:
5954
+ contents: read
5955
+ packages: write
5956
+ id-token: write
5957
+ steps:
5958
+ - uses: actions/checkout@v4
5959
+
5960
+ ${pm.setup}
5961
+ - name: Install
5962
+ run: ${pm.install}
5963
+
5964
+ ${registryLogin(registry)}
5965
+ # A site's public URLs are built into it, so the build reads the stage's
5966
+ # secrets store. With secrets.store on SSM or Secrets Manager the job
5967
+ # assumes the stage's role (AWS_ROLE_ARN, set by gkm deploy:github);
5968
+ # with the default 'file' store there is no role to assume.
5969
+ - name: Assume the stage's AWS role
5970
+ if: needs.stages.outputs.aws-region != ''
5971
+ uses: aws-actions/configure-aws-credentials@v4
5972
+ with:
5973
+ role-to-assume: \${{ vars.AWS_ROLE_ARN }}
5974
+ aws-region: \${{ needs.stages.outputs.aws-region }}
5975
+
5976
+ # Builds and pushes every image the stage runs, tagged with the commit,
5977
+ # and starts nothing.
5978
+ - name: Build and push
5979
+ run: ${pm.exec} gkm compose --stage "$STAGE" --build --push --tag "$SHA" --digests-file digests.json
5980
+ env:
5981
+ STAGE: \${{ matrix.stage }}
5982
+ SHA: \${{ github.sha }}
5983
+
5984
+ # What each tag pointed at when it was pushed. A deploy runs these
5985
+ # digests, so a tag moved since cannot change what is released.
5986
+ - uses: actions/upload-artifact@v4
5987
+ with:
5988
+ name: digests-\${{ matrix.stage }}
5989
+ path: digests.json
5990
+ retention-days: 90
5991
+
5992
+ deploy:
5993
+ name: Deploy \${{ matrix.stage }}
5994
+ needs: [stages, build]
5995
+ # The build is skipped on a release or a manual run: deploy then anyway,
5996
+ # but never after a failed build.
5997
+ if: \${{ !cancelled() && !failure() && needs.stages.outputs.has-deploy == 'true' }}
5998
+ runs-on: ubuntu-latest
5999
+ strategy:
6000
+ fail-fast: false
6001
+ matrix:
6002
+ stage: \${{ fromJSON(needs.stages.outputs.deploy) }}
6003
+ environment: \${{ matrix.stage }}
6004
+ concurrency:
6005
+ group: deploy-\${{ matrix.stage }}
6006
+ cancel-in-progress: false
6007
+ permissions:
6008
+ contents: read
6009
+ actions: read
6010
+ steps:
6011
+ # A release deploys its tag's commit, never its target_commitish — the
6012
+ # branch it was drafted against, which has moved on since.
6013
+ - name: Resolve the commit
6014
+ id: commit
6015
+ run: |
6016
+ case "$EVENT" in
6017
+ release) ref="tags/$TAG" ;;
6018
+ workflow_dispatch) ref="\${REF:-$SHA}" ;;
6019
+ *) ref="$SHA" ;;
6020
+ esac
6021
+ sha=$(gh api "repos/$REPO/commits/$ref" --jq .sha)
6022
+ echo "Deploying $sha"
6023
+ echo "sha=$sha" >> "$GITHUB_OUTPUT"
6024
+ env:
6025
+ GH_TOKEN: \${{ github.token }}
6026
+ REPO: \${{ github.repository }}
6027
+ EVENT: \${{ github.event_name }}
6028
+ TAG: \${{ github.event.release.tag_name }}
6029
+ REF: \${{ inputs.ref }}
6030
+ SHA: \${{ github.sha }}
6031
+
6032
+ # The push run of this workflow that built the commit for this stage:
6033
+ # this run on a push, the newest one with the stage's digests otherwise.
6034
+ - name: Find the commit's push build
6035
+ id: build
5780
6036
  run: |
5781
- ${pm.exec} tsx -e "
5782
- import config from './gkm.config.ts';
5783
- const { deployed, protected: kept = [] } = config.stages;
5784
- const event = process.env.EVENT;
5785
- if (event === 'workflow_dispatch' && !deployed.includes(process.env.STAGE)) {
5786
- console.error('Not a deployed stage: ' + process.env.STAGE + ' (deployed: ' + deployed.join(', ') + ')');
5787
- process.exit(1);
5788
- }
5789
- const stages =
5790
- event === 'workflow_dispatch' ? [process.env.STAGE]
5791
- : event === 'release' ? kept
5792
- : deployed.filter((stage) => !kept.includes(stage));
5793
- console.log('stages=' + JSON.stringify(stages));
5794
- " >> "$GITHUB_OUTPUT"
6037
+ if [ "$EVENT" = push ]; then
6038
+ echo "run-id=$RUN_ID" >> "$GITHUB_OUTPUT"
6039
+ exit 0
6040
+ fi
6041
+ workflow=\${WORKFLOW_REF%%@*}
6042
+ workflow=\${workflow##*/}
6043
+ for id in $(gh run list --repo "$REPO" --workflow "$workflow" --event push --commit "$SHA" --limit 20 --json databaseId --jq '.[].databaseId'); do
6044
+ found=$(gh api "repos/$REPO/actions/runs/$id/artifacts?name=$ARTIFACT" --jq '[.artifacts[] | select(.expired | not)] | length')
6045
+ if [ "$found" -gt 0 ]; then
6046
+ echo "run-id=$id" >> "$GITHUB_OUTPUT"
6047
+ exit 0
6048
+ fi
6049
+ done
6050
+ echo "run-id=" >> "$GITHUB_OUTPUT"
5795
6051
  env:
6052
+ GH_TOKEN: \${{ github.token }}
6053
+ REPO: \${{ github.repository }}
5796
6054
  EVENT: \${{ github.event_name }}
5797
- STAGE: \${{ inputs.stage }}
6055
+ RUN_ID: \${{ github.run_id }}
6056
+ WORKFLOW_REF: \${{ github.workflow_ref }}
6057
+ SHA: \${{ steps.commit.outputs.sha }}
6058
+ ARTIFACT: digests-\${{ matrix.stage }}
6059
+
6060
+ - name: Download digests-\${{ matrix.stage }}
6061
+ if: steps.build.outputs.run-id != ''
6062
+ uses: actions/download-artifact@v4
6063
+ with:
6064
+ name: digests-\${{ matrix.stage }}
6065
+ run-id: \${{ steps.build.outputs.run-id }}
6066
+ github-token: \${{ github.token }}
5798
6067
 
6068
+ - name: No digests — deploying by tag
6069
+ if: steps.build.outputs.run-id == ''
6070
+ run: |
6071
+ echo "::warning title=$STAGE deploys by tag, not digest::No push build of $SHA kept digests-$STAGE (never built, or older than 90 days). The server runs whatever the tag $SHA points at in the registry now, which anyone who can push there could have replaced."
6072
+ env:
6073
+ STAGE: \${{ matrix.stage }}
6074
+ SHA: \${{ steps.commit.outputs.sha }}
6075
+
6076
+ # Each environment holds DEPLOY_SSH_KEY (secret) and DEPLOY_KNOWN_HOSTS,
6077
+ # DEPLOY_HOST, DEPLOY_USER and DEPLOY_PATH (variables): the server's
6078
+ # host key is pinned, never accepted on first sight. DEPLOY_PATH is a
6079
+ # clone of this repository, with a docker login to ${registry.split("/")[0]}
6080
+ # and the stage's secrets.
6081
+ #
6082
+ # Every value reaches the server as an argument to a quoted heredoc —
6083
+ # nothing is pasted into the script.
6084
+ - name: Deploy on the server
6085
+ run: |
6086
+ mkdir -p ~/.ssh && chmod 700 ~/.ssh
6087
+ (umask 077 && printf '%s\\n' "$SSH_KEY" > ~/.ssh/deploy_key)
6088
+ printf '%s\\n' "$KNOWN_HOSTS" > ~/.ssh/known_hosts
6089
+ digests=""
6090
+ if [ -f digests.json ]; then digests=$(base64 -w0 < digests.json); fi
6091
+ ssh -i ~/.ssh/deploy_key -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes \\
6092
+ "$DEPLOY_USER@$DEPLOY_HOST" \\
6093
+ "bash -s -- $(printf '%q ' "$STAGE" "$SHA" "$DEPLOY_PATH" "$digests")" <<'REMOTE'
6094
+ set -euo pipefail
6095
+ stage=$1 sha=$2 dir=$3 digests=$4
6096
+ cd "$dir"
6097
+ git fetch --quiet origin
6098
+ git checkout --quiet --detach "$sha"
6099
+ ${pm.install}
6100
+ args=(--stage "$stage" --tag "$sha")
6101
+ if [ -n "$digests" ]; then
6102
+ mkdir -p .gkm
6103
+ printf '%s' "$digests" | base64 -d > ".gkm/digests-$stage.json"
6104
+ args+=(--digests-file ".gkm/digests-$stage.json")
6105
+ fi
6106
+ ${pm.exec} gkm compose "\${args[@]}"
6107
+ REMOTE
6108
+ env:
6109
+ SSH_KEY: \${{ secrets.DEPLOY_SSH_KEY }}
6110
+ KNOWN_HOSTS: \${{ vars.DEPLOY_KNOWN_HOSTS }}
6111
+ DEPLOY_HOST: \${{ vars.DEPLOY_HOST }}
6112
+ DEPLOY_USER: \${{ vars.DEPLOY_USER }}
6113
+ DEPLOY_PATH: \${{ vars.DEPLOY_PATH }}
6114
+ STAGE: \${{ matrix.stage }}
6115
+ SHA: \${{ steps.commit.outputs.sha }}
6116
+ `;
6117
+ }
6118
+ /**
6119
+ * Dokploy and SST: `gkm deploy` builds, releases and health-checks a stage in
6120
+ * one step, from a checkout, so there is no separate build job — the stages
6121
+ * job's `deploy` list is the matrix, and `build` goes unused.
6122
+ */
6123
+ function targetDeploy(options) {
6124
+ const pm = tooling(options);
6125
+ const sst = options.deployTarget === "sst";
6126
+ const credentials = sst ? `
6127
+ # An IAM role GitHub assumes through OIDC — no long-lived AWS keys. Set
6128
+ # AWS_ROLE_ARN as a variable on each environment.
6129
+ - uses: aws-actions/configure-aws-credentials@v4
6130
+ with:
6131
+ role-to-assume: \${{ vars.AWS_ROLE_ARN }}
6132
+ aws-region: ${options.region}
6133
+ ` : "";
6134
+ const keyDir = `~/.gkm/keys/${projectKey({ name: options.name })}`;
6135
+ const stageSecrets = sst ? credentials : `
6136
+ # The stage's secrets are kept in the encrypted .gkm/secrets/<stage>.json,
6137
+ # and each environment holds its key as GKM_SECRETS_KEY. .gkm/ is
6138
+ # gitignored, so a checkout has the key but not the file: set
6139
+ # secrets.store in gkm.config.ts to a store CI can reach before deploying
6140
+ # from here.
6141
+ # Owner-only, as the CLI writes its own keys. The stage reaches the shell
6142
+ # as a variable, never pasted into the script: on a manual run it is
6143
+ # whatever was typed.
6144
+ - name: Stage secrets key
6145
+ run: |
6146
+ mkdir -p ${keyDir} && chmod 700 ${keyDir}
6147
+ (umask 077 && printf '%s' "$KEY" > ${keyDir}/"$STAGE".key)
6148
+ env:
6149
+ KEY: \${{ secrets.GKM_SECRETS_KEY }}
6150
+ STAGE: \${{ matrix.stage }}
6151
+ `;
6152
+ const deployEnv = sst ? "" : `
6153
+ DOKPLOY_API_TOKEN: \${{ secrets.DOKPLOY_API_TOKEN }}
6154
+ DOKPLOY_ENDPOINT: \${{ vars.DOKPLOY_ENDPOINT }}`;
6155
+ return `${deployHeader(false)}
6156
+ jobs:
6157
+ ${stagesJob(options)}
5799
6158
  deploy:
5800
6159
  name: Deploy \${{ matrix.stage }}
5801
6160
  needs: stages
5802
- if: needs.stages.outputs.stages != '[]'
6161
+ if: needs.stages.outputs.has-deploy == 'true'
5803
6162
  runs-on: ubuntu-latest
5804
6163
  strategy:
5805
6164
  fail-fast: false
5806
6165
  matrix:
5807
- stage: \${{ fromJSON(needs.stages.outputs.stages) }}
6166
+ stage: \${{ fromJSON(needs.stages.outputs.deploy) }}
5808
6167
  # One GitHub environment per stage: its secrets and variables, and any
5809
6168
  # approval rule you put on a protected one.
5810
6169
  environment: \${{ matrix.stage }}
5811
6170
  concurrency:
5812
6171
  group: deploy-\${{ matrix.stage }}
5813
6172
  cancel-in-progress: false
6173
+ permissions:
6174
+ contents: read${sst ? "\n id-token: write" : ""}
5814
6175
  steps:
6176
+ # The event's own commit — a release's tag, the push — or the ref a
6177
+ # manual run names.
5815
6178
  - uses: actions/checkout@v4
6179
+ with:
6180
+ ref: \${{ inputs.ref }}
5816
6181
 
5817
6182
  ${pm.setup}
5818
6183
  - name: Install
@@ -5826,6 +6191,9 @@ ${stageSecrets}
5826
6191
  STAGE: \${{ matrix.stage }}${deployEnv}
5827
6192
  `;
5828
6193
  }
6194
+ function deploy(options) {
6195
+ return options.deployTarget === "compose" ? composeDeploy(options) : targetDeploy(options);
6196
+ }
5829
6197
  function generateGithubFiles(options) {
5830
6198
  const files = [
5831
6199
  {
@@ -6666,6 +7034,11 @@ export type ListUsersResponse = z.infer<typeof ListUsersResponseSchema>;
6666
7034
  const perStage = (options, command) => Object.fromEntries(options.stages.deployed.map((stage) => [`deploy:${stage}`, command(stage)]));
6667
7035
  function deployPackage(options) {
6668
7036
  switch (options.deployTarget) {
7037
+ case "compose": return {
7038
+ scripts: perStage(options, (stage) => `gkm compose --stage ${stage}`),
7039
+ dependencies: {},
7040
+ devDependencies: {}
7041
+ };
6669
7042
  case "dokploy": return {
6670
7043
  scripts: perStage(options, (stage) => `gkm deploy --stage ${stage}`),
6671
7044
  dependencies: {},
@@ -7175,6 +7548,13 @@ ${workspaceConstructsGlobs(options.routesStructure, dirname(options.apiPath)).ma
7175
7548
  * `gkm build` builds for, and which backends a cache or a broker resolves to.
7176
7549
  */
7177
7550
  function deployBlock(options) {
7551
+ if (options.deployTarget === "compose") return `
7552
+ // \`gkm compose --stage <stage>\` runs a stage as one Docker Compose stack on
7553
+ // the machine it runs on. CI builds every image and pushes it here; the
7554
+ // server pulls exactly those. On ghcr.io the owner is the repository's —
7555
+ // the user or organisation whose packages the workflow's token can write.
7556
+ deploy: { default: 'compose', registry: '${options.registry}' },
7557
+ `;
7178
7558
  if (options.deployTarget !== "sst") return "";
7179
7559
  return `
7180
7560
  // \`gkm deploy\` runs \`sst deploy\` on the manifest \`gkm build\` writes to
@@ -9751,6 +10131,11 @@ const deployTargetChoices = [
9751
10131
  value: "dokploy",
9752
10132
  description: "Deploy to Dokploy (Docker-based hosting)"
9753
10133
  },
10134
+ {
10135
+ title: "Server (Docker Compose)",
10136
+ value: "compose",
10137
+ description: "One Docker Compose stack per stage on your own server; CI builds the images"
10138
+ },
9754
10139
  {
9755
10140
  title: "AWS (SST)",
9756
10141
  value: "sst",
@@ -9888,6 +10273,13 @@ async function initCommand(projectName, options = {}) {
9888
10273
  message: "AWS region (e.g. eu-west-1):",
9889
10274
  validate: (value) => AWS_REGION.test(value.trim()) || "An AWS region, like eu-west-1"
9890
10275
  },
10276
+ {
10277
+ type: (_prev, values) => !options.yes && !options.registry && (options.deploy ?? values.deployTarget) === "compose" ? "text" : null,
10278
+ name: "registry",
10279
+ message: "Container registry CI pushes to (e.g. ghcr.io/<your GitHub owner>):",
10280
+ initial: (_prev, values) => defaultRegistry(projectName || options.name || values.name),
10281
+ validate: (value) => value.trim().length > 0 || "A registry, like ghcr.io/acme"
10282
+ },
9891
10283
  {
9892
10284
  type: options.yes || options.stages ? null : "text",
9893
10285
  name: "deployedStages",
@@ -9989,6 +10381,7 @@ async function initCommand(projectName, options = {}) {
9989
10381
  packageManager: pkgManager,
9990
10382
  deployTarget,
9991
10383
  stages: resolveStages(options, answers),
10384
+ ...deployTarget === "compose" ? { registry: options.registry ?? answers.registry?.trim() ?? defaultRegistry(name) } : {},
9992
10385
  ...deployTarget === "sst" ? { region: options.region ?? (options.yes ? "eu-west-1" : answers.region?.trim()) } : {},
9993
10386
  constructs,
9994
10387
  frontendFramework
@@ -10160,6 +10553,13 @@ function printNextSteps(projectName, options, pkgManager) {
10160
10553
  for (const stage of deployed) console.log(` ${getRunCommand(pkgManager, `deploy:${stage}`)}`);
10161
10554
  console.log("");
10162
10555
  }
10556
+ if (options.deployTarget === "compose") {
10557
+ console.log("🚀 Deployment (Docker Compose, on your server):");
10558
+ console.log(` Images are pushed to ${options.registry}; .github/workflows/deploy.yml builds them`);
10559
+ console.log(" and runs gkm compose on the server over SSH. See the comments in it for the");
10560
+ console.log(" environment variables and secrets each stage needs.");
10561
+ console.log("");
10562
+ }
10163
10563
  if (options.deployTarget === "sst") {
10164
10564
  console.log("🚀 Deployment (AWS, through SST):");
10165
10565
  for (const stage of deployed) console.log(` ${getRunCommand(pkgManager, `deploy:${stage}`)} # gkm deploy: build, sst deploy, health checks`);
@@ -10199,6 +10599,14 @@ var UnknownDeployTarget = class extends Error {
10199
10599
  this.name = "UnknownDeployTarget";
10200
10600
  }
10201
10601
  };
10602
+ /**
10603
+ * Where a compose deploy's images go when nobody said: GitHub's registry,
10604
+ * under an owner named after the project. The generated config says to change
10605
+ * it to the repository's owner, whose packages the workflow's token can write.
10606
+ */
10607
+ function defaultRegistry(name) {
10608
+ return `ghcr.io/${(name ?? "my-org").toLowerCase()}`;
10609
+ }
10202
10610
  /** `--region` that is not shaped like one, e.g. `europe` for `eu-west-1`. */
10203
10611
  var NotAnAwsRegion = class extends Error {
10204
10612
  region;
@@ -10903,7 +11311,7 @@ function testDatabases(reconciled) {
10903
11311
  * starts without the reference data the app needs.
10904
11312
  */
10905
11313
  async function migrateTestStage(workspace, env) {
10906
- const { migrateAndSeed } = await import("./migrate-CCejlgU0.mjs");
11314
+ const { migrateAndSeed } = await import("./migrate-CdXcX0XR.mjs");
10907
11315
  const { discover } = await import("./discover-BPelzeGW.mjs").then((n) => n.i);
10908
11316
  const sources = {};
10909
11317
  const manifest = await discover({
@@ -11239,7 +11647,7 @@ const program = new Command();
11239
11647
  program.name("gkm").description("GeekMidas backend framework CLI").version(version).option("--cwd <path>", "Change working directory").option("--debug", "Enable debug mode (verbose errors with stack traces)").hook("preAction", () => {
11240
11648
  if (program.opts().debug);
11241
11649
  });
11242
- program.command("init").description("Scaffold a new project").argument("[name]", "Project name").option("--template <template>", "Project template (minimal, api, serverless, worker)").option("--skip-install", "Skip dependency installation", false).option("-y, --yes", "Skip prompts, use defaults", false).option("--monorepo", "Setup as monorepo with packages/models", false).option("--api-path <path>", "API app path in monorepo (default: apps/api)").option("--pm <manager>", "Package manager (pnpm, npm, yarn, bun)").option("--deploy <target>", "Where it deploys (dokploy, sst, none)").option("--region <region>", "AWS region for an SST deploy (e.g. eu-west-1)").option("--stages <names>", "Deployed stages, comma-separated (e.g. staging,prod)").option("--protected-stage <name>", "Which deployed stage is production").option("--local-stage <name>", "What gkm dev runs as (e.g. dev)").action(async (name, options) => {
11650
+ program.command("init").description("Scaffold a new project").argument("[name]", "Project name").option("--template <template>", "Project template (minimal, api, serverless, worker)").option("--skip-install", "Skip dependency installation", false).option("-y, --yes", "Skip prompts, use defaults", false).option("--monorepo", "Setup as monorepo with packages/models", false).option("--api-path <path>", "API app path in monorepo (default: apps/api)").option("--pm <manager>", "Package manager (pnpm, npm, yarn, bun)").option("--deploy <target>", "Where it deploys (dokploy, compose, sst, none)").option("--region <region>", "AWS region for an SST deploy (e.g. eu-west-1)").option("--registry <registry>", "Container registry for a compose deploy (e.g. ghcr.io/acme)").option("--stages <names>", "Deployed stages, comma-separated (e.g. staging,prod)").option("--protected-stage <name>", "Which deployed stage is production").option("--local-stage <name>", "What gkm dev runs as (e.g. dev)").action(async (name, options) => {
11243
11651
  try {
11244
11652
  const globalOptions = program.opts();
11245
11653
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
@@ -11347,7 +11755,7 @@ program.command("migrate").description("Apply each database construct's migratio
11347
11755
  try {
11348
11756
  const globalOptions = program.opts();
11349
11757
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11350
- const { migrateCommand } = await import("./migrate-CCejlgU0.mjs");
11758
+ const { migrateCommand } = await import("./migrate-CdXcX0XR.mjs");
11351
11759
  await migrateCommand({
11352
11760
  ...options,
11353
11761
  ...construct ? { construct } : {}
@@ -11361,7 +11769,7 @@ program.command("seed").description("Migrate, then run each database construct's
11361
11769
  try {
11362
11770
  const globalOptions = program.opts();
11363
11771
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11364
- const { seedCommand } = await import("./migrate-CCejlgU0.mjs");
11772
+ const { seedCommand } = await import("./migrate-CdXcX0XR.mjs");
11365
11773
  await seedCommand({
11366
11774
  ...options,
11367
11775
  ...construct ? { construct } : {}
@@ -11375,7 +11783,7 @@ program.command("migration").description("Write a construct's next migration: an
11375
11783
  try {
11376
11784
  const globalOptions = program.opts();
11377
11785
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11378
- const { migrationCommand } = await import("./migrate-CCejlgU0.mjs");
11786
+ const { migrationCommand } = await import("./migrate-CdXcX0XR.mjs");
11379
11787
  await migrationCommand({
11380
11788
  construct,
11381
11789
  ...name ? { name } : {}
@@ -11483,7 +11891,7 @@ program.command("secrets:add").description("Build the keys a stage must be given
11483
11891
  try {
11484
11892
  const globalOptions = program.opts();
11485
11893
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11486
- const { secretsAddCommand } = await import("./add-DnYHJOap.mjs");
11894
+ const { secretsAddCommand } = await import("./add-DbnzC7Kl.mjs");
11487
11895
  await secretsAddCommand(options);
11488
11896
  } catch (error) {
11489
11897
  console.error(formatError(error));
@@ -11494,7 +11902,7 @@ program.command("encryption:rotate").description("Add a new current key to an En
11494
11902
  try {
11495
11903
  const globalOptions = program.opts();
11496
11904
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11497
- const { encryptionRotateCommand } = await import("./encryption-DPFljXtM.mjs");
11905
+ const { encryptionRotateCommand } = await import("./encryption-BRcBAmjL.mjs");
11498
11906
  await encryptionRotateCommand(construct, options);
11499
11907
  } catch (error) {
11500
11908
  console.error(formatError(error));
@@ -11505,7 +11913,7 @@ program.command("encryption:retire").description("Remove an old key from an Encr
11505
11913
  try {
11506
11914
  const globalOptions = program.opts();
11507
11915
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11508
- const { encryptionRetireCommand } = await import("./encryption-DPFljXtM.mjs");
11916
+ const { encryptionRetireCommand } = await import("./encryption-BRcBAmjL.mjs");
11509
11917
  await encryptionRetireCommand(construct, key, options);
11510
11918
  } catch (error) {
11511
11919
  console.error(formatError(error));
@@ -11548,7 +11956,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
11548
11956
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11549
11957
  const { loadWorkspaceConfig } = await import("./config.mjs");
11550
11958
  const { reconcileMissingSecrets } = await import("./reconcile--I7V4Klj.mjs");
11551
- const { secretsStoreFor } = await import("./seeds-DbyqaXW4.mjs").then((n) => n._n);
11959
+ const { secretsStoreFor } = await import("./seeds-BK8caVMm.mjs").then((n) => n.hn);
11552
11960
  const { workspace } = await loadWorkspaceConfig();
11553
11961
  const stage = options.stage ?? workspace.stages.local;
11554
11962
  const store = await secretsStoreFor(workspace, stage);
@@ -11557,7 +11965,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
11557
11965
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
11558
11966
  process.exit(1);
11559
11967
  }
11560
- const { derivedContainers } = await import("./seeds-DbyqaXW4.mjs").then((n) => n.X);
11968
+ const { derivedContainers } = await import("./seeds-BK8caVMm.mjs").then((n) => n.X);
11561
11969
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
11562
11970
  if (!result) {
11563
11971
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
@@ -11575,7 +11983,7 @@ program.command("secrets:migrate").description("Copy a deployed stage's secrets
11575
11983
  try {
11576
11984
  const globalOptions = program.opts();
11577
11985
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11578
- const { secretsMigrateCommand } = await import("./migrate-Cd3kcI7o.mjs");
11986
+ const { secretsMigrateCommand } = await import("./migrate-DOMU4uKe.mjs");
11579
11987
  await secretsMigrateCommand(options);
11580
11988
  } catch (error) {
11581
11989
  console.error(formatError(error));
@@ -11583,7 +11991,7 @@ program.command("secrets:migrate").description("Copy a deployed stage's secrets
11583
11991
  }
11584
11992
  });
11585
11993
  program.command("deploy").description("Deploy a stage through its target").option("--target <name>", "Deploy target: dokploy, compose, sst, or one named in deploy.targets (default: deploy.default)").option("--provider <provider>", "[DEPRECATED] Use --target instead").requiredOption("--stage <stage>", "Deployment stage (e.g., production, staging)").option("--tag <tag>", "Image tag (default: stage-timestamp; compose: the commit). Compose pulls a given tag").option("--skip-push", "Skip pushing image to registry").option("--skip-build", "Skip build step (use existing build)").option("--json", "Write events as JSON lines instead of progress; never prompts").option("--dry-run", "Show what would be created or reused; change, build and push nothing").option("--atomic", "If the release fails, roll back every app it released, not only the failed ones").option("--allow-dev-services <list>", "Server targets: run MinIO and/or Mailpit (minio,mailpit) for mail and buckets the stage's secrets don't configure. Not production-grade").action(async (options) => {
11586
- const { deployCli } = await import("./cli-qMszGvDu.mjs");
11994
+ const { deployCli } = await import("./cli-DwTa9d_8.mjs");
11587
11995
  const globalOptions = program.opts();
11588
11996
  const code = await deployCli({
11589
11997
  cwd: resolve(globalOptions.cwd ?? process.cwd()),
@@ -11600,7 +12008,7 @@ program.command("deploy").description("Deploy a stage through its target").optio
11600
12008
  });
11601
12009
  program.command("deploy:rollback").description("Put a stage's app back on its previous release (Dokploy): one app with --app, every app with --atomic").requiredOption("--stage <stage>", "A deployed stage").option("--app <app>", "The app to roll back").option("--atomic", "Roll back every app that has an earlier release").action(async (options) => {
11602
12010
  try {
11603
- const { rollbackStage } = await import("./rollback-DXU6cr7q.mjs").then((n) => n.a);
12011
+ const { rollbackStage } = await import("./rollback-BSdxnW3X.mjs").then((n) => n.a);
11604
12012
  const { terminalCredentials } = await import("./terminal-BdvrmkaC.mjs");
11605
12013
  const globalOptions = program.opts();
11606
12014
  await rollbackStage({
@@ -11636,13 +12044,24 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
11636
12044
  try {
11637
12045
  const globalOptions = program.opts();
11638
12046
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11639
- const { deployGithubCommand } = await import("./github-DL4zwJsR.mjs");
12047
+ const { deployGithubCommand } = await import("./github-Zs-dVJbR.mjs");
11640
12048
  await deployGithubCommand(options);
11641
12049
  } catch (error) {
11642
12050
  console.error(formatError(error));
11643
12051
  process.exit(1);
11644
12052
  }
11645
12053
  });
12054
+ program.command("stages").description("The workspace's stages from gkm.config.ts: local, deployed and protected").option("--json", "Print them as JSON").option("--github-output", "Write the stages, and which this workflow run builds and deploys, to $GITHUB_OUTPUT").option("--event <name>", "The event to plan for (default: $GITHUB_EVENT_NAME)").option("--stage <stage>", "The stage a workflow_dispatch deploys").action(async (options) => {
12055
+ try {
12056
+ const globalOptions = program.opts();
12057
+ if (globalOptions.cwd) process.chdir(globalOptions.cwd);
12058
+ const { stagesCommand } = await import("./stages-CSXt7N2b.mjs");
12059
+ await stagesCommand(options);
12060
+ } catch (error) {
12061
+ console.error(formatError(error));
12062
+ process.exit(1);
12063
+ }
12064
+ });
11646
12065
  program.command("deploy:list").description("List Dokploy resources (projects, registries)").option("--endpoint <url>", "Dokploy server URL (uses stored credentials if logged in)").option("--projects", "List projects").option("--registries", "List registries").action(async (options) => {
11647
12066
  try {
11648
12067
  const globalOptions = program.opts();