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

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 (113) hide show
  1. package/dist/{LegacyStateStore-Ceq7A_75.mjs → LegacyStateStore-DFO3aJgs.mjs} +2 -2
  2. package/dist/{LegacyStateStore-Ceq7A_75.mjs.map → LegacyStateStore-DFO3aJgs.mjs.map} +1 -1
  3. package/dist/{LegacyStateStore-Z-EKUGSX.cjs → LegacyStateStore-iUWVxrXU.cjs} +2 -2
  4. package/dist/{LegacyStateStore-Z-EKUGSX.cjs.map → LegacyStateStore-iUWVxrXU.cjs.map} +1 -1
  5. package/dist/{LocalStateStore-Bocb8dmZ.cjs → LocalStateStore-BsFHdSeA.cjs} +2 -2
  6. package/dist/{LocalStateStore-Bocb8dmZ.cjs.map → LocalStateStore-BsFHdSeA.cjs.map} +1 -1
  7. package/dist/{LocalStateStore-C72bwpG2.mjs → LocalStateStore-DIfuh0Z1.mjs} +2 -2
  8. package/dist/{LocalStateStore-C72bwpG2.mjs.map → LocalStateStore-DIfuh0Z1.mjs.map} +1 -1
  9. package/dist/{S3StateStore-C9upSW5o.mjs → S3StateStore-DR2f78Dj.mjs} +2 -2
  10. package/dist/{S3StateStore-C9upSW5o.mjs.map → S3StateStore-DR2f78Dj.mjs.map} +1 -1
  11. package/dist/{S3StateStore-35YfcdL_.cjs → S3StateStore-ytvpIiv8.cjs} +2 -2
  12. package/dist/{S3StateStore-35YfcdL_.cjs.map → S3StateStore-ytvpIiv8.cjs.map} +1 -1
  13. package/dist/{SSMStateStore-dOmFO6g1.mjs → SSMStateStore-BZ3XL0Mr.mjs} +2 -2
  14. package/dist/{SSMStateStore-dOmFO6g1.mjs.map → SSMStateStore-BZ3XL0Mr.mjs.map} +1 -1
  15. package/dist/{SSMStateStore-W-Ps22AQ.cjs → SSMStateStore-C1JruCD9.cjs} +2 -2
  16. package/dist/{SSMStateStore-W-Ps22AQ.cjs.map → SSMStateStore-C1JruCD9.cjs.map} +1 -1
  17. package/dist/{add-1T3y6E4M.cjs → add-Bwddb4KD.cjs} +3 -3
  18. package/dist/{add-1T3y6E4M.cjs.map → add-Bwddb4KD.cjs.map} +1 -1
  19. package/dist/{add-DnYHJOap.mjs → add-DkBEZ5_s.mjs} +3 -3
  20. package/dist/{add-DnYHJOap.mjs.map → add-DkBEZ5_s.mjs.map} +1 -1
  21. package/dist/{bundler-DlKdxCGh.mjs → bundler-B8TGsunu.mjs} +5 -5
  22. package/dist/{bundler-DlKdxCGh.mjs.map → bundler-B8TGsunu.mjs.map} +1 -1
  23. package/dist/{bundler-YNvLdvlh.cjs → bundler-BkH1kPEX.cjs} +5 -5
  24. package/dist/{bundler-YNvLdvlh.cjs.map → bundler-BkH1kPEX.cjs.map} +1 -1
  25. package/dist/{cli-qMszGvDu.mjs → cli-BgDd4cZB.mjs} +3 -3
  26. package/dist/{cli-qMszGvDu.mjs.map → cli-BgDd4cZB.mjs.map} +1 -1
  27. package/dist/{cli-CWee1-wU.cjs → cli-DO7DQqCq.cjs} +3 -3
  28. package/dist/{cli-CWee1-wU.cjs.map → cli-DO7DQqCq.cjs.map} +1 -1
  29. package/dist/{compose-D4sD4Lgr.mjs → compose-BPJ9bUKp.mjs} +3 -3
  30. package/dist/{compose-D4sD4Lgr.mjs.map → compose-BPJ9bUKp.mjs.map} +1 -1
  31. package/dist/{compose-qnDyPJNp.cjs → compose-CGyxYe-h.cjs} +3 -3
  32. package/dist/{compose-qnDyPJNp.cjs.map → compose-CGyxYe-h.cjs.map} +1 -1
  33. package/dist/{config-L_6NivCp.d.mts → config-BBFiH4qk.d.mts} +2 -2
  34. package/dist/{config-L_6NivCp.d.mts.map → config-BBFiH4qk.d.mts.map} +1 -1
  35. package/dist/{config-Mr4rcXFC.d.cts → config-DMdzdO5u.d.cts} +2 -2
  36. package/dist/{config-Mr4rcXFC.d.cts.map → config-DMdzdO5u.d.cts.map} +1 -1
  37. package/dist/config.cjs +1 -1
  38. package/dist/config.d.cts +2 -2
  39. package/dist/config.d.mts +2 -2
  40. package/dist/config.mjs +1 -1
  41. package/dist/deploy/public.cjs +6 -6
  42. package/dist/deploy/public.d.cts +1 -1
  43. package/dist/deploy/public.d.mts +1 -1
  44. package/dist/deploy/public.mjs +6 -6
  45. package/dist/{deploy-C00DAKOt.mjs → deploy-0XpZDYAr.mjs} +5 -5
  46. package/dist/{deploy-C00DAKOt.mjs.map → deploy-0XpZDYAr.mjs.map} +1 -1
  47. package/dist/{deploy-CXP2fXvK.cjs → deploy-BlZx6J5x.cjs} +5 -5
  48. package/dist/{deploy-CXP2fXvK.cjs.map → deploy-BlZx6J5x.cjs.map} +1 -1
  49. package/dist/{dokploy-B3zoCmJb.mjs → dokploy-Crh4uDmP.mjs} +2 -2
  50. package/dist/{dokploy-B3zoCmJb.mjs.map → dokploy-Crh4uDmP.mjs.map} +1 -1
  51. package/dist/{dokploy-BHcSmghb.cjs → dokploy-rjXmkuwH.cjs} +2 -2
  52. package/dist/{dokploy-BHcSmghb.cjs.map → dokploy-rjXmkuwH.cjs.map} +1 -1
  53. package/dist/{encryption-DPFljXtM.mjs → encryption-0eo9aY_H.mjs} +3 -3
  54. package/dist/{encryption-DPFljXtM.mjs.map → encryption-0eo9aY_H.mjs.map} +1 -1
  55. package/dist/{encryption-DLin89I_.cjs → encryption-CxTfRx4h.cjs} +3 -3
  56. package/dist/{encryption-DLin89I_.cjs.map → encryption-CxTfRx4h.cjs.map} +1 -1
  57. package/dist/{engine-BS3iX9Mu.cjs → engine-CMzSebbs.cjs} +4 -4
  58. package/dist/{engine-BS3iX9Mu.cjs.map → engine-CMzSebbs.cjs.map} +1 -1
  59. package/dist/{engine-CgzgIg-_.mjs → engine-DxWswfXe.mjs} +4 -4
  60. package/dist/{engine-CgzgIg-_.mjs.map → engine-DxWswfXe.mjs.map} +1 -1
  61. package/dist/{github-D8KVmNRg.cjs → github-Dukt5cJe.cjs} +3 -3
  62. package/dist/{github-D8KVmNRg.cjs.map → github-Dukt5cJe.cjs.map} +1 -1
  63. package/dist/{github-DL4zwJsR.mjs → github-HOfOpFI_.mjs} +3 -3
  64. package/dist/{github-DL4zwJsR.mjs.map → github-HOfOpFI_.mjs.map} +1 -1
  65. package/dist/{index-DTEPwLml.d.cts → index-DFrbhRwg.d.cts} +6 -2
  66. package/dist/{index-C0UmP_lg.d.mts.map → index-DFrbhRwg.d.cts.map} +1 -1
  67. package/dist/{index-C0UmP_lg.d.mts → index-Wuqpf316.d.mts} +6 -2
  68. package/dist/{index-DTEPwLml.d.cts.map → index-Wuqpf316.d.mts.map} +1 -1
  69. package/dist/index.cjs +427 -111
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.mjs +427 -111
  72. package/dist/index.mjs.map +1 -1
  73. package/dist/{migrate-CCejlgU0.mjs → migrate-1dGEzuJ-.mjs} +3 -3
  74. package/dist/{migrate-CCejlgU0.mjs.map → migrate-1dGEzuJ-.mjs.map} +1 -1
  75. package/dist/{migrate-BdKhIJgT.cjs → migrate-Bemd4tkz.cjs} +3 -3
  76. package/dist/{migrate-BdKhIJgT.cjs.map → migrate-Bemd4tkz.cjs.map} +1 -1
  77. package/dist/{migrate-DIjG8-QR.cjs → migrate-BgPWVnAZ.cjs} +3 -3
  78. package/dist/{migrate-DIjG8-QR.cjs.map → migrate-BgPWVnAZ.cjs.map} +1 -1
  79. package/dist/{migrate-Cd3kcI7o.mjs → migrate-C14NNbFx.mjs} +3 -3
  80. package/dist/{migrate-Cd3kcI7o.mjs.map → migrate-C14NNbFx.mjs.map} +1 -1
  81. package/dist/openapi.cjs +1 -1
  82. package/dist/openapi.mjs +1 -1
  83. package/dist/{resolve-ApSUK_HB.cjs → resolve-DCTcD823.cjs} +3 -3
  84. package/dist/{resolve-ApSUK_HB.cjs.map → resolve-DCTcD823.cjs.map} +1 -1
  85. package/dist/{resolve-DY9TaVeV.mjs → resolve-y1PkSZrF.mjs} +3 -3
  86. package/dist/{resolve-DY9TaVeV.mjs.map → resolve-y1PkSZrF.mjs.map} +1 -1
  87. package/dist/{rollback-CdGhCk1d.cjs → rollback-BVQFDjxr.cjs} +5 -5
  88. package/dist/{rollback-CdGhCk1d.cjs.map → rollback-BVQFDjxr.cjs.map} +1 -1
  89. package/dist/{rollback-DXU6cr7q.mjs → rollback-CwVhG_xa.mjs} +5 -5
  90. package/dist/{rollback-DXU6cr7q.mjs.map → rollback-CwVhG_xa.mjs.map} +1 -1
  91. package/dist/{seeds-DbyqaXW4.mjs → seeds-BoKQ74Od.mjs} +3 -3
  92. package/dist/{seeds-DbyqaXW4.mjs.map → seeds-BoKQ74Od.mjs.map} +1 -1
  93. package/dist/{seeds-BLzrSwEr.cjs → seeds-Ccju3zGl.cjs} +3 -3
  94. package/dist/{seeds-BLzrSwEr.cjs.map → seeds-Ccju3zGl.cjs.map} +1 -1
  95. package/dist/{sniffer-ByWtH28i.mjs → sniffer-6oYI-MpT.mjs} +6 -6
  96. package/dist/{sniffer-ByWtH28i.mjs.map → sniffer-6oYI-MpT.mjs.map} +1 -1
  97. package/dist/{sniffer-BjG-1mlD.cjs → sniffer-DeyYI9Iv.cjs} +6 -6
  98. package/dist/{sniffer-BjG-1mlD.cjs.map → sniffer-DeyYI9Iv.cjs.map} +1 -1
  99. package/dist/stages-CHF9RFog.mjs +174 -0
  100. package/dist/stages-CHF9RFog.mjs.map +1 -0
  101. package/dist/stages-CwrYVX9u.cjs +174 -0
  102. package/dist/stages-CwrYVX9u.cjs.map +1 -0
  103. package/dist/target/public.cjs +1 -1
  104. package/dist/target/public.mjs +1 -1
  105. package/dist/workspace/index.cjs +2 -1
  106. package/dist/workspace/index.d.cts +2 -2
  107. package/dist/workspace/index.d.mts +2 -2
  108. package/dist/workspace/index.mjs +2 -2
  109. package/dist/{workspace-Bp8FEkF-.cjs → workspace-4i6Jwsoa.cjs} +22 -2
  110. package/dist/{workspace-Bp8FEkF-.cjs.map → workspace-4i6Jwsoa.cjs.map} +1 -1
  111. package/dist/{workspace-BBdqmkch.mjs → workspace-DD9mtPy8.mjs} +11 -3
  112. package/dist/{workspace-BBdqmkch.mjs.map → workspace-DD9mtPy8.mjs.map} +1 -1
  113. package/package.json +15 -15
package/dist/index.cjs CHANGED
@@ -5,20 +5,20 @@ const require_run = require("./run-fVZ-rLnc.cjs");
5
5
  const require_credentials = require("./credentials-BVs7mrtw.cjs");
6
6
  const require_discover = require("./discover-D1It5r_h.cjs");
7
7
  const require_derive = require("./derive-DAbo49sm.cjs");
8
- const require_workspace = require("./workspace-Bp8FEkF-.cjs");
8
+ const require_workspace = require("./workspace-4i6Jwsoa.cjs");
9
9
  const require_config = require("./config.cjs");
10
- const require_seeds = require("./seeds-BLzrSwEr.cjs");
10
+ const require_seeds = require("./seeds-Ccju3zGl.cjs");
11
11
  const require_EndpointGenerator = require("./EndpointGenerator-cKlnllIu.cjs");
12
12
  const require_openapi = require("./openapi.cjs");
13
13
  const require_SubscriberGenerator = require("./SubscriberGenerator-CYriX5BV.cjs");
14
14
  const require_appPorts = require("./appPorts-Cu79hJF6.cjs");
15
15
  const require_drivers = require("./drivers-CJx76mcb.cjs");
16
16
  const require_emit = require("./emit-Dmc1nvdS.cjs");
17
- const require_sniffer = require("./sniffer-BjG-1mlD.cjs");
18
- const require_deploy = require("./deploy-CXP2fXvK.cjs");
19
- const require_compose = require("./compose-qnDyPJNp.cjs");
17
+ const require_sniffer = require("./sniffer-DeyYI9Iv.cjs");
18
+ const require_deploy = require("./deploy-BlZx6J5x.cjs");
19
+ const require_compose = require("./compose-CGyxYe-h.cjs");
20
20
  const require_credentialSchemas = require("./credentialSchemas-DCR4Wd1W.cjs");
21
- const require_LocalStateStore = require("./LocalStateStore-Bocb8dmZ.cjs");
21
+ const require_LocalStateStore = require("./LocalStateStore-BsFHdSeA.cjs");
22
22
  const require_utils = require("./utils-D2Nk1Nv2.cjs");
23
23
  const require_ready = require("./ready-4kx2pAhm.cjs");
24
24
  let node_path = require("node:path");
@@ -53,7 +53,7 @@ crypto = require_rolldown_runtime.__toESM(crypto, 1);
53
53
  let _geekmidas_constructs_functions = require("@geekmidas/constructs/functions");
54
54
  let _geekmidas_constructs_topic = require("@geekmidas/constructs/topic");
55
55
  //#region package.json
56
- var version = "10.0.0-alpha.82";
56
+ var version = "10.0.0-alpha.83";
57
57
  //#endregion
58
58
  //#region src/prompt.ts
59
59
  /**
@@ -136,7 +136,7 @@ const logger$8 = console;
136
136
  * Validate Dokploy token by making a test API call
137
137
  */
138
138
  async function validateDokployToken(endpoint, token) {
139
- const { DokployApi } = await Promise.resolve().then(() => require("./sniffer-BjG-1mlD.cjs")).then((n) => n.dokploy_api_exports);
139
+ const { DokployApi } = await Promise.resolve().then(() => require("./sniffer-DeyYI9Iv.cjs")).then((n) => n.dokploy_api_exports);
140
140
  return new DokployApi({
141
141
  baseUrl: endpoint,
142
142
  token
@@ -2217,9 +2217,9 @@ async function workspaceDevCommand(configured, options) {
2217
2217
  for (const { id, port } of fakes) logger$5.log(`🎭 ${id} fake: http://localhost:${port}`);
2218
2218
  logger$5.log(options.migrate || options.seed ? "🗄️ Migrating the databases" : "🗄️ Checking for pending migrations");
2219
2219
  if (options.migrate || options.seed) {
2220
- const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-DIjG8-QR.cjs"));
2220
+ const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-BgPWVnAZ.cjs"));
2221
2221
  await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
2222
- } else await Promise.resolve().then(() => require("./migrate-DIjG8-QR.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
2222
+ } else await Promise.resolve().then(() => require("./migrate-BgPWVnAZ.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
2223
2223
  logger$5.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
2224
2224
  });
2225
2225
  const appUrls = {};
@@ -3227,7 +3227,7 @@ async function buildApp(input) {
3227
3227
  if (production?.bundle && !input.skipBundle) {
3228
3228
  const outputDir = (0, node_path.join)(appRoot, ".gkm", "server");
3229
3229
  const entryPoint = await writeSurfaceServer(outputDir, production);
3230
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-YNvLdvlh.cjs"));
3230
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-BkH1kPEX.cjs"));
3231
3231
  await bundleServer({
3232
3232
  entryPoint,
3233
3233
  outputDir: (0, node_path.join)(outputDir, "dist"),
@@ -3351,7 +3351,7 @@ async function buildWorkers(input) {
3351
3351
  });
3352
3352
  logger$4.log(`Generated worker ${worker.id}: ${crons.length} crons, ${queues.length} queues, ${subscribers.length} subscribers`);
3353
3353
  if (!input.bundle) continue;
3354
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-YNvLdvlh.cjs"));
3354
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-BkH1kPEX.cjs"));
3355
3355
  const outfile = require_seeds.workerBundleName(worker.id);
3356
3356
  await bundleServer({
3357
3357
  entryPoint,
@@ -3421,7 +3421,7 @@ async function buildForTarget(target, context, appRoot, workspaceRoot, endpointG
3421
3421
  let masterKey;
3422
3422
  if (context.production?.bundle && !skipBundle) {
3423
3423
  logger$4.log(`\n📦 Bundling production server...`);
3424
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-YNvLdvlh.cjs"));
3424
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-BkH1kPEX.cjs"));
3425
3425
  const allConstructs = [
3426
3426
  ...endpoints.map((e) => e.construct),
3427
3427
  ...functions.map((f) => f.construct),
@@ -5108,12 +5108,12 @@ const PNPM_VERSION = "pnpm@10.13.1";
5108
5108
  const BIOME_SCHEMA = `https://biomejs.dev/schemas/${DEPENDENCY_VERSIONS["@biomejs/biome"].replace(/^[~^]/, "")}/schema.json`;
5109
5109
  //#endregion
5110
5110
  //#region src/init/versions.ts
5111
- const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
5111
+ const require$2 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
5112
5112
  function loadPackageJson() {
5113
5113
  try {
5114
- return require$1("../package.json");
5114
+ return require$2("../package.json");
5115
5115
  } catch {
5116
- return require$1("../../package.json");
5116
+ return require$2("../../package.json");
5117
5117
  }
5118
5118
  }
5119
5119
  /**
@@ -5123,25 +5123,25 @@ function loadPackageJson() {
5123
5123
  * Run: pnpm --filter @geekmidas/cli sync-versions
5124
5124
  */
5125
5125
  const GEEKMIDAS_VERSIONS = {
5126
- "@geekmidas/audit": "~10.0.0-alpha.82",
5127
- "@geekmidas/auth": "~10.0.0-alpha.82",
5128
- "@geekmidas/cache": "~10.0.0-alpha.82",
5129
- "@geekmidas/client": "~10.0.0-alpha.82",
5130
- "@geekmidas/cloud": "~10.0.0-alpha.82",
5131
- "@geekmidas/constructs": "~10.0.0-alpha.82",
5132
- "@geekmidas/db": "~10.0.0-alpha.82",
5133
- "@geekmidas/emailkit": "~10.0.0-alpha.82",
5134
- "@geekmidas/envkit": "~10.0.0-alpha.82",
5135
- "@geekmidas/errors": "~10.0.0-alpha.82",
5136
- "@geekmidas/events": "~10.0.0-alpha.82",
5137
- "@geekmidas/logger": "~10.0.0-alpha.82",
5138
- "@geekmidas/manifest": "~10.0.0-alpha.82",
5139
- "@geekmidas/rate-limit": "~10.0.0-alpha.82",
5140
- "@geekmidas/schema": "~10.0.0-alpha.82",
5141
- "@geekmidas/services": "~10.0.0-alpha.82",
5142
- "@geekmidas/storage": "~10.0.0-alpha.82",
5143
- "@geekmidas/telescope": "~10.0.0-alpha.82",
5144
- "@geekmidas/testkit": "~10.0.0-alpha.82",
5126
+ "@geekmidas/audit": "~10.0.0-alpha.83",
5127
+ "@geekmidas/auth": "~10.0.0-alpha.83",
5128
+ "@geekmidas/cache": "~10.0.0-alpha.83",
5129
+ "@geekmidas/client": "~10.0.0-alpha.83",
5130
+ "@geekmidas/cloud": "~10.0.0-alpha.83",
5131
+ "@geekmidas/constructs": "~10.0.0-alpha.83",
5132
+ "@geekmidas/db": "~10.0.0-alpha.83",
5133
+ "@geekmidas/emailkit": "~10.0.0-alpha.83",
5134
+ "@geekmidas/envkit": "~10.0.0-alpha.83",
5135
+ "@geekmidas/errors": "~10.0.0-alpha.83",
5136
+ "@geekmidas/events": "~10.0.0-alpha.83",
5137
+ "@geekmidas/logger": "~10.0.0-alpha.83",
5138
+ "@geekmidas/manifest": "~10.0.0-alpha.83",
5139
+ "@geekmidas/rate-limit": "~10.0.0-alpha.83",
5140
+ "@geekmidas/schema": "~10.0.0-alpha.83",
5141
+ "@geekmidas/services": "~10.0.0-alpha.83",
5142
+ "@geekmidas/storage": "~10.0.0-alpha.83",
5143
+ "@geekmidas/telescope": "~10.0.0-alpha.83",
5144
+ "@geekmidas/testkit": "~10.0.0-alpha.83",
5145
5145
  "@geekmidas/cli": `~${loadPackageJson().version}`
5146
5146
  };
5147
5147
  //#endregion
@@ -5514,6 +5514,38 @@ coverage/
5514
5514
  return files;
5515
5515
  }
5516
5516
  //#endregion
5517
+ //#region src/init/stagesAction.ts
5518
+ /**
5519
+ * How a scaffolded workflow names the `stages` action.
5520
+ *
5521
+ * A published CLI pins it to the commit it was released from: the release
5522
+ * build is given that commit as `GKM_RELEASE_COMMIT`, which the build writes
5523
+ * into the bundle (`define` in tsdown.config.ts). The action at that commit is
5524
+ * the one released beside this CLI, and a commit SHA cannot be moved under a
5525
+ * project the way a branch or a tag can.
5526
+ *
5527
+ * A CLI built anywhere else — a checkout, a local build — knows no released
5528
+ * commit, and names `main`.
5529
+ */
5530
+ const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
5531
+ function cliVersion() {
5532
+ try {
5533
+ return require$1("../package.json").version;
5534
+ } catch {
5535
+ return require$1("../../package.json").version;
5536
+ }
5537
+ }
5538
+ /** The action, as a workflow's `uses:` names it before the `@`. */
5539
+ const STAGES_ACTION = "geekmidas/toolbox/actions/stages";
5540
+ const COMMIT = /^[0-9a-f]{40}$/;
5541
+ /**
5542
+ * The `uses:` value for the stages action, with a comment saying which CLI
5543
+ * release the pinned commit is.
5544
+ */
5545
+ function stagesActionUses(commit = "4dee60bc593ffdfbaa59bbf8d8fe9e3e64f23d41", version = cliVersion()) {
5546
+ return commit && COMMIT.test(commit) ? `${STAGES_ACTION}@${commit} # @geekmidas/cli ${version}` : `${STAGES_ACTION}@main # pin to a commit SHA: see the action's README`;
5547
+ }
5548
+ //#endregion
5517
5549
  //#region src/init/generators/github.ts
5518
5550
  /**
5519
5551
  * The GitHub workflows a scaffold ships with.
@@ -5523,11 +5555,14 @@ coverage/
5523
5555
  * release, labelled from their titles.
5524
5556
  * - `deploy.yml` — when a deploy target was picked: a push to main deploys the
5525
5557
  * stages that are not protected, publishing the drafted release deploys the
5526
- * protected ones.
5558
+ * protected ones, and a manual run deploys the one stage it names.
5527
5559
  *
5528
- * The deploy workflow names no stage. It reads `stages` from gkm.config.ts at
5529
- * run time, so the config stays the one place stages are declared and a
5530
- * workflow cannot fall out of step with it.
5560
+ * The deploy workflow names no stage. Its first job runs the
5561
+ * `geekmidas/toolbox/actions/stages` action, which asks the project's own
5562
+ * `gkm stages --github-output` which stages the run builds and deploys — so
5563
+ * the config stays the one place stages are declared, the rules for each event
5564
+ * are tested code in the CLI, and a workflow cannot fall out of step with
5565
+ * either.
5531
5566
  */
5532
5567
  /** How each package manager is set up, installs, and runs a script. */
5533
5568
  const TOOLING = {
@@ -5713,46 +5748,21 @@ template: |
5713
5748
  **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
5714
5749
  `;
5715
5750
  }
5716
- function deploy(options) {
5717
- const pm = tooling(options);
5718
- const sst = options.deployTarget === "sst";
5719
- const credentials = sst ? `
5720
- # An IAM role GitHub assumes through OIDC — no long-lived AWS keys. Set
5721
- # AWS_ROLE_ARN as a variable on each environment.
5722
- - uses: aws-actions/configure-aws-credentials@v4
5723
- with:
5724
- role-to-assume: \${{ vars.AWS_ROLE_ARN }}
5725
- aws-region: ${options.region}
5726
- ` : "";
5727
- const keyDir = `~/.gkm/keys/${require_seeds.projectKey({ name: options.name })}`;
5728
- const stageSecrets = sst ? credentials : `
5729
- # The stage's secrets are kept in the encrypted .gkm/secrets/<stage>.json,
5730
- # and each environment holds its key as GKM_SECRETS_KEY. .gkm/ is
5731
- # gitignored, so a checkout has the key but not the file: set
5732
- # secrets.store in gkm.config.ts to a store CI can reach before deploying
5733
- # from here.
5734
- # Owner-only, as the CLI writes its own keys. The stage reaches the shell
5735
- # as a variable, never pasted into the script: on a manual run it is
5736
- # whatever was typed.
5737
- - name: Stage secrets key
5738
- run: |
5739
- mkdir -p -m 700 ${keyDir}
5740
- (umask 077 && printf '%s' "$KEY" > ${keyDir}/"$STAGE".key)
5741
- env:
5742
- KEY: \${{ secrets.GKM_SECRETS_KEY }}
5743
- STAGE: \${{ matrix.stage }}
5744
- `;
5745
- const deployEnv = sst ? "" : `
5746
- DOKPLOY_API_TOKEN: \${{ secrets.DOKPLOY_API_TOKEN }}
5747
- DOKPLOY_ENDPOINT: \${{ vars.DOKPLOY_ENDPOINT }}`;
5751
+ /**
5752
+ * What every deploy workflow starts with: its triggers, and the `stages` job,
5753
+ * which asks the project's own gkm (through the stages action) which stages
5754
+ * this run builds and deploys. Nothing in a workflow names a stage.
5755
+ */
5756
+ function deployHeader(build) {
5748
5757
  return `name: Deploy
5749
5758
 
5750
5759
  # Which stages exist, and which are protected, is read from \`stages\` in
5751
- # gkm.config.ts — nothing here names one:
5760
+ # gkm.config.ts by the project's own gkm, through the stages action — nothing
5761
+ # here names one:
5752
5762
  #
5753
- # push to main every deployed stage that is not protected
5763
+ # push to main ${build ? "builds every deployed stage, deploys those not protected" : "every deployed stage that is not protected"}
5754
5764
  # release published the protected stages (publish the drafted release)
5755
- # run workflow the one stage you name
5765
+ # run workflow the one stage you name, at the ref you name
5756
5766
  on:
5757
5767
  push:
5758
5768
  branches: [main]
@@ -5763,17 +5773,27 @@ on:
5763
5773
  stage:
5764
5774
  description: A deployed stage from gkm.config.ts
5765
5775
  required: true
5776
+ type: string
5777
+ ref:
5778
+ description: The commit, tag or branch to deploy (default the latest on main)
5779
+ required: false
5780
+ type: string
5766
5781
 
5767
5782
  permissions:
5768
5783
  contents: read
5769
- id-token: write
5770
-
5771
- jobs:
5772
- stages:
5784
+ `;
5785
+ }
5786
+ function stagesJob(options) {
5787
+ const pm = tooling(options);
5788
+ return ` stages:
5773
5789
  name: Pick stages
5774
5790
  runs-on: ubuntu-latest
5775
5791
  outputs:
5776
- stages: \${{ steps.pick.outputs.stages }}
5792
+ build: \${{ steps.stages.outputs.build }}
5793
+ deploy: \${{ steps.stages.outputs.deploy }}
5794
+ has-build: \${{ steps.stages.outputs.has-build }}
5795
+ has-deploy: \${{ steps.stages.outputs.has-deploy }}
5796
+ aws-region: \${{ steps.stages.outputs.aws-region }}
5777
5797
  steps:
5778
5798
  - uses: actions/checkout@v4
5779
5799
 
@@ -5781,44 +5801,286 @@ ${pm.setup}
5781
5801
  - name: Install
5782
5802
  run: ${pm.install}
5783
5803
 
5804
+ # Runs \`gkm stages --github-output\`: the rules for each event are the
5805
+ # installed CLI's, and a manual run naming a stage gkm.config.ts does not
5806
+ # deploy fails here.
5784
5807
  - name: Read stages from gkm.config.ts
5785
- id: pick
5808
+ id: stages
5809
+ uses: ${stagesActionUses()}
5810
+ with:
5811
+ stage: \${{ inputs.stage }}
5812
+ `;
5813
+ }
5814
+ /**
5815
+ * The registry login a compose build pushes with: GitHub's own token on
5816
+ * ghcr.io, otherwise a username variable and a password secret.
5817
+ */
5818
+ function registryLogin(registry) {
5819
+ const host = registry.split("/")[0];
5820
+ const ghcr = host === "ghcr.io";
5821
+ return ` - uses: docker/login-action@v3
5822
+ with:
5823
+ registry: ${host}
5824
+ username: ${ghcr ? "${{ github.actor }}" : "${{ vars.REGISTRY_USERNAME }}"}
5825
+ password: ${ghcr ? "${{ secrets.GITHUB_TOKEN }}" : "${{ secrets.REGISTRY_PASSWORD }}"}
5826
+ `;
5827
+ }
5828
+ /**
5829
+ * The compose deploy: images are built and pushed by the runner, where the
5830
+ * code is, and pulled by the server, where the stage runs.
5831
+ *
5832
+ * - `build` (on a push): every deployed stage's images, tagged with the
5833
+ * commit, and their digests kept as the artifact `digests-<stage>`.
5834
+ * - `deploy`: the commit the event names — a release's tag, a manual run's
5835
+ * `ref` — then that commit's push build, whose digests pin each image. Over
5836
+ * SSH, with a pinned host key, the server checks the commit out and runs
5837
+ * `gkm compose` on exactly those images.
5838
+ */
5839
+ function composeDeploy(options) {
5840
+ const pm = tooling(options);
5841
+ const registry = options.registry ?? "";
5842
+ return `${deployHeader(true)}
5843
+ jobs:
5844
+ ${stagesJob(options)}
5845
+ build:
5846
+ name: Build \${{ matrix.stage }}
5847
+ needs: stages
5848
+ if: needs.stages.outputs.has-build == 'true'
5849
+ runs-on: ubuntu-latest
5850
+ strategy:
5851
+ fail-fast: false
5852
+ matrix:
5853
+ stage: \${{ fromJSON(needs.stages.outputs.build) }}
5854
+ # One GitHub environment per stage: its secrets, variables and approvals.
5855
+ environment: \${{ matrix.stage }}
5856
+ permissions:
5857
+ contents: read
5858
+ packages: write
5859
+ id-token: write
5860
+ steps:
5861
+ - uses: actions/checkout@v4
5862
+
5863
+ ${pm.setup}
5864
+ - name: Install
5865
+ run: ${pm.install}
5866
+
5867
+ ${registryLogin(registry)}
5868
+ # A site's public URLs are built into it, so the build reads the stage's
5869
+ # secrets store. With secrets.store on SSM or Secrets Manager the job
5870
+ # assumes the stage's role (AWS_ROLE_ARN, set by gkm deploy:github);
5871
+ # with the default 'file' store there is no role to assume.
5872
+ - name: Assume the stage's AWS role
5873
+ if: needs.stages.outputs.aws-region != ''
5874
+ uses: aws-actions/configure-aws-credentials@v4
5875
+ with:
5876
+ role-to-assume: \${{ vars.AWS_ROLE_ARN }}
5877
+ aws-region: \${{ needs.stages.outputs.aws-region }}
5878
+
5879
+ # Builds and pushes every image the stage runs, tagged with the commit,
5880
+ # and starts nothing.
5881
+ - name: Build and push
5882
+ run: ${pm.exec} gkm compose --stage "$STAGE" --build --push --tag "$SHA" --digests-file digests.json
5883
+ env:
5884
+ STAGE: \${{ matrix.stage }}
5885
+ SHA: \${{ github.sha }}
5886
+
5887
+ # What each tag pointed at when it was pushed. A deploy runs these
5888
+ # digests, so a tag moved since cannot change what is released.
5889
+ - uses: actions/upload-artifact@v4
5890
+ with:
5891
+ name: digests-\${{ matrix.stage }}
5892
+ path: digests.json
5893
+ retention-days: 90
5894
+
5895
+ deploy:
5896
+ name: Deploy \${{ matrix.stage }}
5897
+ needs: [stages, build]
5898
+ # The build is skipped on a release or a manual run: deploy then anyway,
5899
+ # but never after a failed build.
5900
+ if: \${{ !cancelled() && !failure() && needs.stages.outputs.has-deploy == 'true' }}
5901
+ runs-on: ubuntu-latest
5902
+ strategy:
5903
+ fail-fast: false
5904
+ matrix:
5905
+ stage: \${{ fromJSON(needs.stages.outputs.deploy) }}
5906
+ environment: \${{ matrix.stage }}
5907
+ concurrency:
5908
+ group: deploy-\${{ matrix.stage }}
5909
+ cancel-in-progress: false
5910
+ permissions:
5911
+ contents: read
5912
+ actions: read
5913
+ steps:
5914
+ # A release deploys its tag's commit, never its target_commitish — the
5915
+ # branch it was drafted against, which has moved on since.
5916
+ - name: Resolve the commit
5917
+ id: commit
5918
+ run: |
5919
+ case "$EVENT" in
5920
+ release) ref="tags/$TAG" ;;
5921
+ workflow_dispatch) ref="\${REF:-$SHA}" ;;
5922
+ *) ref="$SHA" ;;
5923
+ esac
5924
+ sha=$(gh api "repos/$REPO/commits/$ref" --jq .sha)
5925
+ echo "Deploying $sha"
5926
+ echo "sha=$sha" >> "$GITHUB_OUTPUT"
5927
+ env:
5928
+ GH_TOKEN: \${{ github.token }}
5929
+ REPO: \${{ github.repository }}
5930
+ EVENT: \${{ github.event_name }}
5931
+ TAG: \${{ github.event.release.tag_name }}
5932
+ REF: \${{ inputs.ref }}
5933
+ SHA: \${{ github.sha }}
5934
+
5935
+ # The push run of this workflow that built the commit for this stage:
5936
+ # this run on a push, the newest one with the stage's digests otherwise.
5937
+ - name: Find the commit's push build
5938
+ id: build
5786
5939
  run: |
5787
- ${pm.exec} tsx -e "
5788
- import config from './gkm.config.ts';
5789
- const { deployed, protected: kept = [] } = config.stages;
5790
- const event = process.env.EVENT;
5791
- if (event === 'workflow_dispatch' && !deployed.includes(process.env.STAGE)) {
5792
- console.error('Not a deployed stage: ' + process.env.STAGE + ' (deployed: ' + deployed.join(', ') + ')');
5793
- process.exit(1);
5794
- }
5795
- const stages =
5796
- event === 'workflow_dispatch' ? [process.env.STAGE]
5797
- : event === 'release' ? kept
5798
- : deployed.filter((stage) => !kept.includes(stage));
5799
- console.log('stages=' + JSON.stringify(stages));
5800
- " >> "$GITHUB_OUTPUT"
5940
+ if [ "$EVENT" = push ]; then
5941
+ echo "run-id=$RUN_ID" >> "$GITHUB_OUTPUT"
5942
+ exit 0
5943
+ fi
5944
+ workflow=\${WORKFLOW_REF%%@*}
5945
+ workflow=\${workflow##*/}
5946
+ for id in $(gh run list --repo "$REPO" --workflow "$workflow" --event push --commit "$SHA" --limit 20 --json databaseId --jq '.[].databaseId'); do
5947
+ found=$(gh api "repos/$REPO/actions/runs/$id/artifacts?name=$ARTIFACT" --jq '[.artifacts[] | select(.expired | not)] | length')
5948
+ if [ "$found" -gt 0 ]; then
5949
+ echo "run-id=$id" >> "$GITHUB_OUTPUT"
5950
+ exit 0
5951
+ fi
5952
+ done
5953
+ echo "run-id=" >> "$GITHUB_OUTPUT"
5801
5954
  env:
5955
+ GH_TOKEN: \${{ github.token }}
5956
+ REPO: \${{ github.repository }}
5802
5957
  EVENT: \${{ github.event_name }}
5803
- STAGE: \${{ inputs.stage }}
5958
+ RUN_ID: \${{ github.run_id }}
5959
+ WORKFLOW_REF: \${{ github.workflow_ref }}
5960
+ SHA: \${{ steps.commit.outputs.sha }}
5961
+ ARTIFACT: digests-\${{ matrix.stage }}
5962
+
5963
+ - name: Download digests-\${{ matrix.stage }}
5964
+ if: steps.build.outputs.run-id != ''
5965
+ uses: actions/download-artifact@v4
5966
+ with:
5967
+ name: digests-\${{ matrix.stage }}
5968
+ run-id: \${{ steps.build.outputs.run-id }}
5969
+ github-token: \${{ github.token }}
5804
5970
 
5971
+ - name: No digests — deploying by tag
5972
+ if: steps.build.outputs.run-id == ''
5973
+ run: |
5974
+ 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."
5975
+ env:
5976
+ STAGE: \${{ matrix.stage }}
5977
+ SHA: \${{ steps.commit.outputs.sha }}
5978
+
5979
+ # Each environment holds DEPLOY_SSH_KEY (secret) and DEPLOY_KNOWN_HOSTS,
5980
+ # DEPLOY_HOST, DEPLOY_USER and DEPLOY_PATH (variables): the server's
5981
+ # host key is pinned, never accepted on first sight. DEPLOY_PATH is a
5982
+ # clone of this repository, with a docker login to ${registry.split("/")[0]}
5983
+ # and the stage's secrets.
5984
+ #
5985
+ # Every value reaches the server as an argument to a quoted heredoc —
5986
+ # nothing is pasted into the script.
5987
+ - name: Deploy on the server
5988
+ run: |
5989
+ mkdir -p ~/.ssh && chmod 700 ~/.ssh
5990
+ (umask 077 && printf '%s\\n' "$SSH_KEY" > ~/.ssh/deploy_key)
5991
+ printf '%s\\n' "$KNOWN_HOSTS" > ~/.ssh/known_hosts
5992
+ digests=""
5993
+ if [ -f digests.json ]; then digests=$(base64 -w0 < digests.json); fi
5994
+ ssh -i ~/.ssh/deploy_key -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes \\
5995
+ "$DEPLOY_USER@$DEPLOY_HOST" \\
5996
+ "bash -s -- $(printf '%q ' "$STAGE" "$SHA" "$DEPLOY_PATH" "$digests")" <<'REMOTE'
5997
+ set -euo pipefail
5998
+ stage=$1 sha=$2 dir=$3 digests=$4
5999
+ cd "$dir"
6000
+ git fetch --quiet origin
6001
+ git checkout --quiet --detach "$sha"
6002
+ ${pm.install}
6003
+ args=(--stage "$stage" --tag "$sha")
6004
+ if [ -n "$digests" ]; then
6005
+ mkdir -p .gkm
6006
+ printf '%s' "$digests" | base64 -d > ".gkm/digests-$stage.json"
6007
+ args+=(--digests-file ".gkm/digests-$stage.json")
6008
+ fi
6009
+ ${pm.exec} gkm compose "\${args[@]}"
6010
+ REMOTE
6011
+ env:
6012
+ SSH_KEY: \${{ secrets.DEPLOY_SSH_KEY }}
6013
+ KNOWN_HOSTS: \${{ vars.DEPLOY_KNOWN_HOSTS }}
6014
+ DEPLOY_HOST: \${{ vars.DEPLOY_HOST }}
6015
+ DEPLOY_USER: \${{ vars.DEPLOY_USER }}
6016
+ DEPLOY_PATH: \${{ vars.DEPLOY_PATH }}
6017
+ STAGE: \${{ matrix.stage }}
6018
+ SHA: \${{ steps.commit.outputs.sha }}
6019
+ `;
6020
+ }
6021
+ /**
6022
+ * Dokploy and SST: `gkm deploy` builds, releases and health-checks a stage in
6023
+ * one step, from a checkout, so there is no separate build job — the stages
6024
+ * job's `deploy` list is the matrix, and `build` goes unused.
6025
+ */
6026
+ function targetDeploy(options) {
6027
+ const pm = tooling(options);
6028
+ const sst = options.deployTarget === "sst";
6029
+ const credentials = sst ? `
6030
+ # An IAM role GitHub assumes through OIDC — no long-lived AWS keys. Set
6031
+ # AWS_ROLE_ARN as a variable on each environment.
6032
+ - uses: aws-actions/configure-aws-credentials@v4
6033
+ with:
6034
+ role-to-assume: \${{ vars.AWS_ROLE_ARN }}
6035
+ aws-region: ${options.region}
6036
+ ` : "";
6037
+ const keyDir = `~/.gkm/keys/${require_seeds.projectKey({ name: options.name })}`;
6038
+ const stageSecrets = sst ? credentials : `
6039
+ # The stage's secrets are kept in the encrypted .gkm/secrets/<stage>.json,
6040
+ # and each environment holds its key as GKM_SECRETS_KEY. .gkm/ is
6041
+ # gitignored, so a checkout has the key but not the file: set
6042
+ # secrets.store in gkm.config.ts to a store CI can reach before deploying
6043
+ # from here.
6044
+ # Owner-only, as the CLI writes its own keys. The stage reaches the shell
6045
+ # as a variable, never pasted into the script: on a manual run it is
6046
+ # whatever was typed.
6047
+ - name: Stage secrets key
6048
+ run: |
6049
+ mkdir -p ${keyDir} && chmod 700 ${keyDir}
6050
+ (umask 077 && printf '%s' "$KEY" > ${keyDir}/"$STAGE".key)
6051
+ env:
6052
+ KEY: \${{ secrets.GKM_SECRETS_KEY }}
6053
+ STAGE: \${{ matrix.stage }}
6054
+ `;
6055
+ const deployEnv = sst ? "" : `
6056
+ DOKPLOY_API_TOKEN: \${{ secrets.DOKPLOY_API_TOKEN }}
6057
+ DOKPLOY_ENDPOINT: \${{ vars.DOKPLOY_ENDPOINT }}`;
6058
+ return `${deployHeader(false)}
6059
+ jobs:
6060
+ ${stagesJob(options)}
5805
6061
  deploy:
5806
6062
  name: Deploy \${{ matrix.stage }}
5807
6063
  needs: stages
5808
- if: needs.stages.outputs.stages != '[]'
6064
+ if: needs.stages.outputs.has-deploy == 'true'
5809
6065
  runs-on: ubuntu-latest
5810
6066
  strategy:
5811
6067
  fail-fast: false
5812
6068
  matrix:
5813
- stage: \${{ fromJSON(needs.stages.outputs.stages) }}
6069
+ stage: \${{ fromJSON(needs.stages.outputs.deploy) }}
5814
6070
  # One GitHub environment per stage: its secrets and variables, and any
5815
6071
  # approval rule you put on a protected one.
5816
6072
  environment: \${{ matrix.stage }}
5817
6073
  concurrency:
5818
6074
  group: deploy-\${{ matrix.stage }}
5819
6075
  cancel-in-progress: false
6076
+ permissions:
6077
+ contents: read${sst ? "\n id-token: write" : ""}
5820
6078
  steps:
6079
+ # The event's own commit — a release's tag, the push — or the ref a
6080
+ # manual run names.
5821
6081
  - uses: actions/checkout@v4
6082
+ with:
6083
+ ref: \${{ inputs.ref }}
5822
6084
 
5823
6085
  ${pm.setup}
5824
6086
  - name: Install
@@ -5832,6 +6094,9 @@ ${stageSecrets}
5832
6094
  STAGE: \${{ matrix.stage }}${deployEnv}
5833
6095
  `;
5834
6096
  }
6097
+ function deploy(options) {
6098
+ return options.deployTarget === "compose" ? composeDeploy(options) : targetDeploy(options);
6099
+ }
5835
6100
  function generateGithubFiles(options) {
5836
6101
  const files = [
5837
6102
  {
@@ -6672,6 +6937,11 @@ export type ListUsersResponse = z.infer<typeof ListUsersResponseSchema>;
6672
6937
  const perStage = (options, command) => Object.fromEntries(options.stages.deployed.map((stage) => [`deploy:${stage}`, command(stage)]));
6673
6938
  function deployPackage(options) {
6674
6939
  switch (options.deployTarget) {
6940
+ case "compose": return {
6941
+ scripts: perStage(options, (stage) => `gkm compose --stage ${stage}`),
6942
+ dependencies: {},
6943
+ devDependencies: {}
6944
+ };
6675
6945
  case "dokploy": return {
6676
6946
  scripts: perStage(options, (stage) => `gkm deploy --stage ${stage}`),
6677
6947
  dependencies: {},
@@ -7181,6 +7451,13 @@ ${workspaceConstructsGlobs(options.routesStructure, (0, node_path.dirname)(optio
7181
7451
  * `gkm build` builds for, and which backends a cache or a broker resolves to.
7182
7452
  */
7183
7453
  function deployBlock(options) {
7454
+ if (options.deployTarget === "compose") return `
7455
+ // \`gkm compose --stage <stage>\` runs a stage as one Docker Compose stack on
7456
+ // the machine it runs on. CI builds every image and pushes it here; the
7457
+ // server pulls exactly those. On ghcr.io the owner is the repository's —
7458
+ // the user or organisation whose packages the workflow's token can write.
7459
+ deploy: { default: 'compose', registry: '${options.registry}' },
7460
+ `;
7184
7461
  if (options.deployTarget !== "sst") return "";
7185
7462
  return `
7186
7463
  // \`gkm deploy\` runs \`sst deploy\` on the manifest \`gkm build\` writes to
@@ -9757,6 +10034,11 @@ const deployTargetChoices = [
9757
10034
  value: "dokploy",
9758
10035
  description: "Deploy to Dokploy (Docker-based hosting)"
9759
10036
  },
10037
+ {
10038
+ title: "Server (Docker Compose)",
10039
+ value: "compose",
10040
+ description: "One Docker Compose stack per stage on your own server; CI builds the images"
10041
+ },
9760
10042
  {
9761
10043
  title: "AWS (SST)",
9762
10044
  value: "sst",
@@ -9894,6 +10176,13 @@ async function initCommand(projectName, options = {}) {
9894
10176
  message: "AWS region (e.g. eu-west-1):",
9895
10177
  validate: (value) => AWS_REGION.test(value.trim()) || "An AWS region, like eu-west-1"
9896
10178
  },
10179
+ {
10180
+ type: (_prev, values) => !options.yes && !options.registry && (options.deploy ?? values.deployTarget) === "compose" ? "text" : null,
10181
+ name: "registry",
10182
+ message: "Container registry CI pushes to (e.g. ghcr.io/<your GitHub owner>):",
10183
+ initial: (_prev, values) => defaultRegistry(projectName || options.name || values.name),
10184
+ validate: (value) => value.trim().length > 0 || "A registry, like ghcr.io/acme"
10185
+ },
9897
10186
  {
9898
10187
  type: options.yes || options.stages ? null : "text",
9899
10188
  name: "deployedStages",
@@ -9995,6 +10284,7 @@ async function initCommand(projectName, options = {}) {
9995
10284
  packageManager: pkgManager,
9996
10285
  deployTarget,
9997
10286
  stages: resolveStages(options, answers),
10287
+ ...deployTarget === "compose" ? { registry: options.registry ?? answers.registry?.trim() ?? defaultRegistry(name) } : {},
9998
10288
  ...deployTarget === "sst" ? { region: options.region ?? (options.yes ? "eu-west-1" : answers.region?.trim()) } : {},
9999
10289
  constructs,
10000
10290
  frontendFramework
@@ -10166,6 +10456,13 @@ function printNextSteps(projectName, options, pkgManager) {
10166
10456
  for (const stage of deployed) console.log(` ${require_utils.getRunCommand(pkgManager, `deploy:${stage}`)}`);
10167
10457
  console.log("");
10168
10458
  }
10459
+ if (options.deployTarget === "compose") {
10460
+ console.log("🚀 Deployment (Docker Compose, on your server):");
10461
+ console.log(` Images are pushed to ${options.registry}; .github/workflows/deploy.yml builds them`);
10462
+ console.log(" and runs gkm compose on the server over SSH. See the comments in it for the");
10463
+ console.log(" environment variables and secrets each stage needs.");
10464
+ console.log("");
10465
+ }
10169
10466
  if (options.deployTarget === "sst") {
10170
10467
  console.log("🚀 Deployment (AWS, through SST):");
10171
10468
  for (const stage of deployed) console.log(` ${require_utils.getRunCommand(pkgManager, `deploy:${stage}`)} # gkm deploy: build, sst deploy, health checks`);
@@ -10205,6 +10502,14 @@ var UnknownDeployTarget = class extends Error {
10205
10502
  this.name = "UnknownDeployTarget";
10206
10503
  }
10207
10504
  };
10505
+ /**
10506
+ * Where a compose deploy's images go when nobody said: GitHub's registry,
10507
+ * under an owner named after the project. The generated config says to change
10508
+ * it to the repository's owner, whose packages the workflow's token can write.
10509
+ */
10510
+ function defaultRegistry(name) {
10511
+ return `ghcr.io/${(name ?? "my-org").toLowerCase()}`;
10512
+ }
10208
10513
  /** `--region` that is not shaped like one, e.g. `europe` for `eu-west-1`. */
10209
10514
  var NotAnAwsRegion = class extends Error {
10210
10515
  region;
@@ -10909,7 +11214,7 @@ function testDatabases(reconciled) {
10909
11214
  * starts without the reference data the app needs.
10910
11215
  */
10911
11216
  async function migrateTestStage(workspace, env) {
10912
- const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-DIjG8-QR.cjs"));
11217
+ const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-BgPWVnAZ.cjs"));
10913
11218
  const { discover } = await Promise.resolve().then(() => require("./discover-D1It5r_h.cjs")).then((n) => n.discover_exports);
10914
11219
  const sources = {};
10915
11220
  const manifest = await discover({
@@ -11245,7 +11550,7 @@ const program = new commander.Command();
11245
11550
  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", () => {
11246
11551
  if (program.opts().debug);
11247
11552
  });
11248
- 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) => {
11553
+ 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) => {
11249
11554
  try {
11250
11555
  const globalOptions = program.opts();
11251
11556
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
@@ -11353,7 +11658,7 @@ program.command("migrate").description("Apply each database construct's migratio
11353
11658
  try {
11354
11659
  const globalOptions = program.opts();
11355
11660
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11356
- const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-DIjG8-QR.cjs"));
11661
+ const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-BgPWVnAZ.cjs"));
11357
11662
  await migrateCommand({
11358
11663
  ...options,
11359
11664
  ...construct ? { construct } : {}
@@ -11367,7 +11672,7 @@ program.command("seed").description("Migrate, then run each database construct's
11367
11672
  try {
11368
11673
  const globalOptions = program.opts();
11369
11674
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11370
- const { seedCommand } = await Promise.resolve().then(() => require("./migrate-DIjG8-QR.cjs"));
11675
+ const { seedCommand } = await Promise.resolve().then(() => require("./migrate-BgPWVnAZ.cjs"));
11371
11676
  await seedCommand({
11372
11677
  ...options,
11373
11678
  ...construct ? { construct } : {}
@@ -11381,7 +11686,7 @@ program.command("migration").description("Write a construct's next migration: an
11381
11686
  try {
11382
11687
  const globalOptions = program.opts();
11383
11688
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11384
- const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-DIjG8-QR.cjs"));
11689
+ const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-BgPWVnAZ.cjs"));
11385
11690
  await migrationCommand({
11386
11691
  construct,
11387
11692
  ...name ? { name } : {}
@@ -11489,7 +11794,7 @@ program.command("secrets:add").description("Build the keys a stage must be given
11489
11794
  try {
11490
11795
  const globalOptions = program.opts();
11491
11796
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11492
- const { secretsAddCommand } = await Promise.resolve().then(() => require("./add-1T3y6E4M.cjs"));
11797
+ const { secretsAddCommand } = await Promise.resolve().then(() => require("./add-Bwddb4KD.cjs"));
11493
11798
  await secretsAddCommand(options);
11494
11799
  } catch (error) {
11495
11800
  console.error(formatError(error));
@@ -11500,7 +11805,7 @@ program.command("encryption:rotate").description("Add a new current key to an En
11500
11805
  try {
11501
11806
  const globalOptions = program.opts();
11502
11807
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11503
- const { encryptionRotateCommand } = await Promise.resolve().then(() => require("./encryption-DLin89I_.cjs"));
11808
+ const { encryptionRotateCommand } = await Promise.resolve().then(() => require("./encryption-CxTfRx4h.cjs"));
11504
11809
  await encryptionRotateCommand(construct, options);
11505
11810
  } catch (error) {
11506
11811
  console.error(formatError(error));
@@ -11511,7 +11816,7 @@ program.command("encryption:retire").description("Remove an old key from an Encr
11511
11816
  try {
11512
11817
  const globalOptions = program.opts();
11513
11818
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11514
- const { encryptionRetireCommand } = await Promise.resolve().then(() => require("./encryption-DLin89I_.cjs"));
11819
+ const { encryptionRetireCommand } = await Promise.resolve().then(() => require("./encryption-CxTfRx4h.cjs"));
11515
11820
  await encryptionRetireCommand(construct, key, options);
11516
11821
  } catch (error) {
11517
11822
  console.error(formatError(error));
@@ -11554,7 +11859,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
11554
11859
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11555
11860
  const { loadWorkspaceConfig } = await Promise.resolve().then(() => require("./config.cjs"));
11556
11861
  const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CuCQBa5-.cjs"));
11557
- const { secretsStoreFor } = await Promise.resolve().then(() => require("./seeds-BLzrSwEr.cjs")).then((n) => n.store_exports);
11862
+ const { secretsStoreFor } = await Promise.resolve().then(() => require("./seeds-Ccju3zGl.cjs")).then((n) => n.store_exports);
11558
11863
  const { workspace } = await loadWorkspaceConfig();
11559
11864
  const stage = options.stage ?? workspace.stages.local;
11560
11865
  const store = await secretsStoreFor(workspace, stage);
@@ -11563,7 +11868,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
11563
11868
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
11564
11869
  process.exit(1);
11565
11870
  }
11566
- const { derivedContainers } = await Promise.resolve().then(() => require("./seeds-BLzrSwEr.cjs")).then((n) => n.workspace_exports);
11871
+ const { derivedContainers } = await Promise.resolve().then(() => require("./seeds-Ccju3zGl.cjs")).then((n) => n.workspace_exports);
11567
11872
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
11568
11873
  if (!result) {
11569
11874
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
@@ -11581,7 +11886,7 @@ program.command("secrets:migrate").description("Copy a deployed stage's secrets
11581
11886
  try {
11582
11887
  const globalOptions = program.opts();
11583
11888
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11584
- const { secretsMigrateCommand } = await Promise.resolve().then(() => require("./migrate-BdKhIJgT.cjs"));
11889
+ const { secretsMigrateCommand } = await Promise.resolve().then(() => require("./migrate-Bemd4tkz.cjs"));
11585
11890
  await secretsMigrateCommand(options);
11586
11891
  } catch (error) {
11587
11892
  console.error(formatError(error));
@@ -11589,7 +11894,7 @@ program.command("secrets:migrate").description("Copy a deployed stage's secrets
11589
11894
  }
11590
11895
  });
11591
11896
  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) => {
11592
- const { deployCli } = await Promise.resolve().then(() => require("./cli-CWee1-wU.cjs"));
11897
+ const { deployCli } = await Promise.resolve().then(() => require("./cli-DO7DQqCq.cjs"));
11593
11898
  const globalOptions = program.opts();
11594
11899
  const code = await deployCli({
11595
11900
  cwd: (0, node_path.resolve)(globalOptions.cwd ?? process.cwd()),
@@ -11606,7 +11911,7 @@ program.command("deploy").description("Deploy a stage through its target").optio
11606
11911
  });
11607
11912
  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) => {
11608
11913
  try {
11609
- const { rollbackStage } = await Promise.resolve().then(() => require("./rollback-CdGhCk1d.cjs")).then((n) => n.rollback_exports);
11914
+ const { rollbackStage } = await Promise.resolve().then(() => require("./rollback-BVQFDjxr.cjs")).then((n) => n.rollback_exports);
11610
11915
  const { terminalCredentials } = await Promise.resolve().then(() => require("./terminal-BL5E0xbR.cjs"));
11611
11916
  const globalOptions = program.opts();
11612
11917
  await rollbackStage({
@@ -11642,13 +11947,24 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
11642
11947
  try {
11643
11948
  const globalOptions = program.opts();
11644
11949
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11645
- const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-D8KVmNRg.cjs"));
11950
+ const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-Dukt5cJe.cjs"));
11646
11951
  await deployGithubCommand(options);
11647
11952
  } catch (error) {
11648
11953
  console.error(formatError(error));
11649
11954
  process.exit(1);
11650
11955
  }
11651
11956
  });
11957
+ 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) => {
11958
+ try {
11959
+ const globalOptions = program.opts();
11960
+ if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11961
+ const { stagesCommand } = await Promise.resolve().then(() => require("./stages-CwrYVX9u.cjs"));
11962
+ await stagesCommand(options);
11963
+ } catch (error) {
11964
+ console.error(formatError(error));
11965
+ process.exit(1);
11966
+ }
11967
+ });
11652
11968
  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) => {
11653
11969
  try {
11654
11970
  const globalOptions = program.opts();