@geekmidas/cli 10.0.0-alpha.74 → 10.0.0-alpha.76

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 (161) hide show
  1. package/dist/{LegacyStateStore-D_78eaHM.cjs → LegacyStateStore-DwngfJxr.cjs} +2 -2
  2. package/dist/{LegacyStateStore-D_78eaHM.cjs.map → LegacyStateStore-DwngfJxr.cjs.map} +1 -1
  3. package/dist/{LegacyStateStore-DTo7z924.mjs → LegacyStateStore-dyq07_Ir.mjs} +2 -2
  4. package/dist/{LegacyStateStore-DTo7z924.mjs.map → LegacyStateStore-dyq07_Ir.mjs.map} +1 -1
  5. package/dist/{LocalStateStore-BhrlQIXD.cjs → LocalStateStore-B0NCde8Z.cjs} +2 -2
  6. package/dist/{LocalStateStore-BhrlQIXD.cjs.map → LocalStateStore-B0NCde8Z.cjs.map} +1 -1
  7. package/dist/{LocalStateStore-CNjgkRk4.mjs → LocalStateStore-DLxCCatI.mjs} +2 -2
  8. package/dist/{LocalStateStore-CNjgkRk4.mjs.map → LocalStateStore-DLxCCatI.mjs.map} +1 -1
  9. package/dist/{S3StateStore-oCxRs6HM.cjs → S3StateStore-BOenb-3o.cjs} +2 -2
  10. package/dist/{S3StateStore-oCxRs6HM.cjs.map → S3StateStore-BOenb-3o.cjs.map} +1 -1
  11. package/dist/{S3StateStore-g2P7T_Mo.mjs → S3StateStore-CGK_4hlo.mjs} +2 -2
  12. package/dist/{S3StateStore-g2P7T_Mo.mjs.map → S3StateStore-CGK_4hlo.mjs.map} +1 -1
  13. package/dist/{SSMStateStore-CYenunmK.mjs → SSMStateStore-B_5_RqY-.mjs} +2 -2
  14. package/dist/{SSMStateStore-CYenunmK.mjs.map → SSMStateStore-B_5_RqY-.mjs.map} +1 -1
  15. package/dist/{SSMStateStore-DXp0m7XI.cjs → SSMStateStore-DYeD__7p.cjs} +2 -2
  16. package/dist/{SSMStateStore-DXp0m7XI.cjs.map → SSMStateStore-DYeD__7p.cjs.map} +1 -1
  17. package/dist/{add-BTH7WaH3.mjs → add-B7IMXzou.mjs} +3 -3
  18. package/dist/{add-BTH7WaH3.mjs.map → add-B7IMXzou.mjs.map} +1 -1
  19. package/dist/{add-Djrr12Xc.cjs → add-j2yI3cbS.cjs} +3 -3
  20. package/dist/{add-Djrr12Xc.cjs.map → add-j2yI3cbS.cjs.map} +1 -1
  21. package/dist/{aws-DojzQnLd.mjs → aws-Bja4zGSs.mjs} +13 -9
  22. package/dist/aws-Bja4zGSs.mjs.map +1 -0
  23. package/dist/{aws-DkHdTV-p.cjs → aws-CQAuluiu.cjs} +13 -9
  24. package/dist/aws-CQAuluiu.cjs.map +1 -0
  25. package/dist/awsStore-7VmS_cZR.cjs +54 -0
  26. package/dist/awsStore-7VmS_cZR.cjs.map +1 -0
  27. package/dist/awsStore-B529csle.mjs +43 -0
  28. package/dist/awsStore-B529csle.mjs.map +1 -0
  29. package/dist/{bundler-utHuESvL.cjs → bundler-Cf6OgaOe.cjs} +5 -5
  30. package/dist/bundler-Cf6OgaOe.cjs.map +1 -0
  31. package/dist/{bundler-BUojOkof.mjs → bundler-DO6bHhnb.mjs} +5 -5
  32. package/dist/bundler-DO6bHhnb.mjs.map +1 -0
  33. package/dist/{cli-CyYceKTS.mjs → cli-BCPOHmg9.mjs} +3 -3
  34. package/dist/{cli-CyYceKTS.mjs.map → cli-BCPOHmg9.mjs.map} +1 -1
  35. package/dist/{cli-Bx0sUzxH.cjs → cli-CNanUB7a.cjs} +3 -3
  36. package/dist/{cli-Bx0sUzxH.cjs.map → cli-CNanUB7a.cjs.map} +1 -1
  37. package/dist/{compose-DZp8DJ0A.mjs → compose-Dq4VCtu5.mjs} +221 -153
  38. package/dist/compose-Dq4VCtu5.mjs.map +1 -0
  39. package/dist/{compose-iuw8wfHk.cjs → compose-h_6oNnHC.cjs} +243 -151
  40. package/dist/compose-h_6oNnHC.cjs.map +1 -0
  41. package/dist/{config-B38YWicj.d.cts → config-CWyhLSY1.d.cts} +3 -3
  42. package/dist/{config-B38YWicj.d.cts.map → config-CWyhLSY1.d.cts.map} +1 -1
  43. package/dist/{config-DA0kRTQe.d.mts → config-CqAvPP-f.d.mts} +3 -3
  44. package/dist/{config-DA0kRTQe.d.mts.map → config-CqAvPP-f.d.mts.map} +1 -1
  45. package/dist/config.cjs +1 -1
  46. package/dist/config.d.cts +3 -3
  47. package/dist/config.d.mts +3 -3
  48. package/dist/config.mjs +1 -1
  49. package/dist/deploy/public.cjs +7 -6
  50. package/dist/deploy/public.d.cts +14 -3
  51. package/dist/deploy/public.d.cts.map +1 -1
  52. package/dist/deploy/public.d.mts +14 -3
  53. package/dist/deploy/public.d.mts.map +1 -1
  54. package/dist/deploy/public.mjs +7 -7
  55. package/dist/{deploy-eWorhvQp.cjs → deploy-CT3R9de9.cjs} +27 -5
  56. package/dist/deploy-CT3R9de9.cjs.map +1 -0
  57. package/dist/{deploy-C17K7Imy.mjs → deploy-CVXEx7Nm.mjs} +22 -6
  58. package/dist/deploy-CVXEx7Nm.mjs.map +1 -0
  59. package/dist/{dokploy-DKa49Nqm.mjs → dokploy-CYm2ozFs.mjs} +2 -2
  60. package/dist/{dokploy-DKa49Nqm.mjs.map → dokploy-CYm2ozFs.mjs.map} +1 -1
  61. package/dist/{dokploy-DKzcI-RL.cjs → dokploy-eSGmlhhj.cjs} +2 -2
  62. package/dist/{dokploy-DKzcI-RL.cjs.map → dokploy-eSGmlhhj.cjs.map} +1 -1
  63. package/dist/{encryption-sTMz3aWi.mjs → encryption-DC0rsR1W.mjs} +3 -3
  64. package/dist/{encryption-sTMz3aWi.mjs.map → encryption-DC0rsR1W.mjs.map} +1 -1
  65. package/dist/{encryption-DSkJdoyk.cjs → encryption-vS3Qzw73.cjs} +3 -3
  66. package/dist/{encryption-DSkJdoyk.cjs.map → encryption-vS3Qzw73.cjs.map} +1 -1
  67. package/dist/{engine-B1b4I34Z.mjs → engine-BOO5TWZd.mjs} +193 -5
  68. package/dist/engine-BOO5TWZd.mjs.map +1 -0
  69. package/dist/{engine-Bh76sAHy.cjs → engine-C0N2d1i5.cjs} +196 -8
  70. package/dist/engine-C0N2d1i5.cjs.map +1 -0
  71. package/dist/{github-C9ODoyZU.mjs → github-CEaFDL6t.mjs} +6 -6
  72. package/dist/github-CEaFDL6t.mjs.map +1 -0
  73. package/dist/{github-BvN3CfxM.cjs → github-ClkdCOL9.cjs} +6 -6
  74. package/dist/github-ClkdCOL9.cjs.map +1 -0
  75. package/dist/{index-B-1lLGzy.d.mts → index-Ccu0f_2m.d.cts} +2 -2
  76. package/dist/{index-B-1lLGzy.d.mts.map → index-Ccu0f_2m.d.cts.map} +1 -1
  77. package/dist/{index-vMidfqhU.d.cts → index-DOxmDKqv.d.mts} +2 -2
  78. package/dist/{index-vMidfqhU.d.cts.map → index-DOxmDKqv.d.mts.map} +1 -1
  79. package/dist/index.cjs +106 -52
  80. package/dist/index.cjs.map +1 -1
  81. package/dist/index.mjs +105 -51
  82. package/dist/index.mjs.map +1 -1
  83. package/dist/migrate-BRbYtFXG.cjs +93 -0
  84. package/dist/migrate-BRbYtFXG.cjs.map +1 -0
  85. package/dist/migrate-D3NAT7Ns.mjs +93 -0
  86. package/dist/migrate-D3NAT7Ns.mjs.map +1 -0
  87. package/dist/{migrate-B0FCZNkp.cjs → migrate-D5_-ylI1.cjs} +3 -3
  88. package/dist/{migrate-B0FCZNkp.cjs.map → migrate-D5_-ylI1.cjs.map} +1 -1
  89. package/dist/{migrate-CTtJhGV9.mjs → migrate-DrO2p9Rl.mjs} +3 -3
  90. package/dist/{migrate-CTtJhGV9.mjs.map → migrate-DrO2p9Rl.mjs.map} +1 -1
  91. package/dist/openapi.cjs +16 -6
  92. package/dist/openapi.cjs.map +1 -1
  93. package/dist/openapi.d.cts +1 -1
  94. package/dist/openapi.d.cts.map +1 -1
  95. package/dist/openapi.d.mts +1 -1
  96. package/dist/openapi.d.mts.map +1 -1
  97. package/dist/openapi.mjs +16 -6
  98. package/dist/openapi.mjs.map +1 -1
  99. package/dist/reconcile/public.d.cts +1 -1
  100. package/dist/reconcile/public.d.mts +1 -1
  101. package/dist/{resolve-DSAXj_47.mjs → resolve-C1muFos5.mjs} +3 -3
  102. package/dist/{resolve-DSAXj_47.mjs.map → resolve-C1muFos5.mjs.map} +1 -1
  103. package/dist/{resolve-D_BU-wKA.cjs → resolve-Fa1Ii0BK.cjs} +3 -3
  104. package/dist/{resolve-D_BU-wKA.cjs.map → resolve-Fa1Ii0BK.cjs.map} +1 -1
  105. package/dist/{rollback-DJy3t696.cjs → rollback-C9N5lKVf.cjs} +5 -5
  106. package/dist/{rollback-DJy3t696.cjs.map → rollback-C9N5lKVf.cjs.map} +1 -1
  107. package/dist/{rollback-CWYTUhfW.mjs → rollback-DeQzbRqM.mjs} +5 -5
  108. package/dist/{rollback-CWYTUhfW.mjs.map → rollback-DeQzbRqM.mjs.map} +1 -1
  109. package/dist/{runtime-BzZD5HHi.d.cts → runtime-BSvzPqXW.d.mts} +2 -2
  110. package/dist/{runtime-BzZD5HHi.d.cts.map → runtime-BSvzPqXW.d.mts.map} +1 -1
  111. package/dist/{runtime-Cm6J6L7n.d.mts → runtime-Cm7QA53H.d.cts} +2 -2
  112. package/dist/{runtime-Cm6J6L7n.d.mts.map → runtime-Cm7QA53H.d.cts.map} +1 -1
  113. package/dist/secretsManager-DUXW3fQt.cjs +70 -0
  114. package/dist/secretsManager-DUXW3fQt.cjs.map +1 -0
  115. package/dist/secretsManager-Ti0oZwsS.mjs +70 -0
  116. package/dist/secretsManager-Ti0oZwsS.mjs.map +1 -0
  117. package/dist/{sniffer-BFmdZpZ2.mjs → sniffer-DR8AlRCk.mjs} +67 -15
  118. package/dist/sniffer-DR8AlRCk.mjs.map +1 -0
  119. package/dist/{sniffer-C-T7dSMN.cjs → sniffer-Y6GQOCXB.cjs} +83 -13
  120. package/dist/sniffer-Y6GQOCXB.cjs.map +1 -0
  121. package/dist/{state-DCwRWyh-.cjs → state-C2qi0iQw.cjs} +241 -334
  122. package/dist/state-C2qi0iQw.cjs.map +1 -0
  123. package/dist/{state-DpkxSwzI.mjs → state-CUecOCNJ.mjs} +213 -306
  124. package/dist/state-CUecOCNJ.mjs.map +1 -0
  125. package/dist/target/public.cjs +1 -1
  126. package/dist/target/public.d.cts +2 -2
  127. package/dist/target/public.d.mts +2 -2
  128. package/dist/target/public.mjs +1 -1
  129. package/dist/{types-TzCChNrA.d.cts → types-BdptGBFS.d.cts} +59 -6
  130. package/dist/types-BdptGBFS.d.cts.map +1 -0
  131. package/dist/{types-Dp9lgx_S.d.mts → types-OxE0OAn2.d.mts} +59 -6
  132. package/dist/types-OxE0OAn2.d.mts.map +1 -0
  133. package/dist/workspace/index.cjs +1 -1
  134. package/dist/workspace/index.d.cts +2 -2
  135. package/dist/workspace/index.d.mts +2 -2
  136. package/dist/workspace/index.mjs +1 -1
  137. package/dist/{workspace-vdrcRI-m.cjs → workspace-DOm81oUo.cjs} +54 -1
  138. package/dist/workspace-DOm81oUo.cjs.map +1 -0
  139. package/dist/{workspace-C7NqCsQF.mjs → workspace-QUTqYmVy.mjs} +43 -2
  140. package/dist/workspace-QUTqYmVy.mjs.map +1 -0
  141. package/package.json +18 -17
  142. package/dist/aws-DkHdTV-p.cjs.map +0 -1
  143. package/dist/aws-DojzQnLd.mjs.map +0 -1
  144. package/dist/bundler-BUojOkof.mjs.map +0 -1
  145. package/dist/bundler-utHuESvL.cjs.map +0 -1
  146. package/dist/compose-DZp8DJ0A.mjs.map +0 -1
  147. package/dist/compose-iuw8wfHk.cjs.map +0 -1
  148. package/dist/deploy-C17K7Imy.mjs.map +0 -1
  149. package/dist/deploy-eWorhvQp.cjs.map +0 -1
  150. package/dist/engine-B1b4I34Z.mjs.map +0 -1
  151. package/dist/engine-Bh76sAHy.cjs.map +0 -1
  152. package/dist/github-BvN3CfxM.cjs.map +0 -1
  153. package/dist/github-C9ODoyZU.mjs.map +0 -1
  154. package/dist/sniffer-BFmdZpZ2.mjs.map +0 -1
  155. package/dist/sniffer-C-T7dSMN.cjs.map +0 -1
  156. package/dist/state-DCwRWyh-.cjs.map +0 -1
  157. package/dist/state-DpkxSwzI.mjs.map +0 -1
  158. package/dist/types-Dp9lgx_S.d.mts.map +0 -1
  159. package/dist/types-TzCChNrA.d.cts.map +0 -1
  160. package/dist/workspace-C7NqCsQF.mjs.map +0 -1
  161. package/dist/workspace-vdrcRI-m.cjs.map +0 -1
@@ -2,9 +2,9 @@ const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
2
  const require_run = require("./run-fVZ-rLnc.cjs");
3
3
  const require_discover = require("./discover-D1It5r_h.cjs");
4
4
  const require_derive = require("./derive-DAbo49sm.cjs");
5
- const require_workspace = require("./workspace-vdrcRI-m.cjs");
5
+ const require_workspace = require("./workspace-DOm81oUo.cjs");
6
6
  const require_define = require("./define-DzRz8FgH.cjs");
7
- const require_state = require("./state-DCwRWyh-.cjs");
7
+ const require_state = require("./state-C2qi0iQw.cjs");
8
8
  const require_SubscriberGenerator = require("./SubscriberGenerator-CajwOa5W.cjs");
9
9
  const require_drivers = require("./drivers-BdvtCKc7.cjs");
10
10
  const require_databases = require("./databases-C2p5LGAp.cjs");
@@ -64,6 +64,33 @@ var ServicePortUnknown = class extends Error {
64
64
  this.name = "ServicePortUnknown";
65
65
  }
66
66
  };
67
+ /**
68
+ * Whether `ref` names a registry on this machine's loopback — which the
69
+ * daemon pulls from and pushes to over plain HTTP by default, and `docker
70
+ * manifest inspect` reaches only when told `--insecure` (without it, a local
71
+ * registry's image is reported as no such manifest).
72
+ */
73
+ function isLoopbackRegistry(ref) {
74
+ const first = ref.split("/")[0];
75
+ if (!ref.includes("/") || !/[.:]|^localhost$/.test(first)) return false;
76
+ const host = first.replace(/:\d+$/, "");
77
+ return host === "localhost" || /^127(?:\.\d{1,3}){3}$/.test(host);
78
+ }
79
+ /** `docker push` finished without saying what digest the registry stored. */
80
+ var PushDigestUnknown = class extends Error {
81
+ ref;
82
+ output;
83
+ constructor(ref, output) {
84
+ super(`Pushed ${ref}, but docker did not say which digest the registry stored (it said: ${output.trim().split("\n").slice(-3).join(" / ") || "nothing"}). Check the push with \`docker buildx imagetools inspect ${ref}\`.`);
85
+ this.ref = ref;
86
+ this.output = output;
87
+ this.name = "PushDigestUnknown";
88
+ }
89
+ };
90
+ /** The digest `docker push` reports last: `<tag>: digest: sha256:… size: …`. */
91
+ function pushedDigest(output) {
92
+ return [...output.matchAll(/digest: (sha256:[0-9a-f]{64})/g)].at(-1)?.[1];
93
+ }
67
94
  function compose(stack, args) {
68
95
  return [
69
96
  "compose",
@@ -127,6 +154,7 @@ const dockerCompose = {
127
154
  const { code, stdout, stderr } = await capture("docker", [
128
155
  "manifest",
129
156
  "inspect",
157
+ ...isLoopbackRegistry(ref) ? ["--insecure"] : [],
130
158
  ref
131
159
  ]);
132
160
  if (code === 0) return {
@@ -196,6 +224,15 @@ const dockerCompose = {
196
224
  to
197
225
  ]), runOptions(stack));
198
226
  },
227
+ async push(stack, ref) {
228
+ const output = await require_run.runOutput("docker", ["push", ref], {
229
+ cwd: stack.cwd,
230
+ ...stack.signal ? { signal: stack.signal } : {}
231
+ });
232
+ const digest = pushedDigest(output);
233
+ if (!digest) throw new PushDigestUnknown(ref, output);
234
+ return digest;
235
+ },
199
236
  async digest(ref) {
200
237
  const { code, stdout } = await capture("docker", [
201
238
  "image",
@@ -266,6 +303,92 @@ async function assertImagesExist(docker, tag, images) {
266
303
  const missing = lookups.filter((lookup) => lookup.status === "missing").map((lookup) => lookup.ref);
267
304
  if (missing.length > 0) throw new ImageTagNotFound(tag, missing);
268
305
  }
306
+ /**
307
+ * A push or a pull with no `deploy.registry` to push to or pull from.
308
+ *
309
+ * Without one an image is named `<namespace>/<project>-<app>:<tag>`, which
310
+ * Docker resolves to Docker Hub — so a release would be pushed to, or pulled
311
+ * from, somebody else's account. It is never a fallback.
312
+ */
313
+ var RegistryRequired = class extends Error {
314
+ operation;
315
+ ref;
316
+ constructor(operation, ref) {
317
+ super(`${operation === "push" ? "Pushing" : "Pulling"} images needs a registry, and gkm.config.ts sets no deploy.registry: '${ref}' would be ${operation === "push" ? "pushed to" : "pulled from"} Docker Hub. Set deploy.registry (e.g. registry: 'ghcr.io/acme') in gkm.config.ts.` + (operation === "pull" ? " To run images built here instead, pass --build." : ""));
318
+ this.operation = operation;
319
+ this.ref = ref;
320
+ this.name = "RegistryRequired";
321
+ }
322
+ };
323
+ /** An image pinned to its digest: `<ref>@sha256:…`. */
324
+ function pinnedRef(ref, digest) {
325
+ return `${ref.split("@")[0]}@${digest}`;
326
+ }
327
+ /** A digests file with no entry for an app the stack runs. */
328
+ var ImageDigestMissing = class extends Error {
329
+ file;
330
+ apps;
331
+ constructor(file, apps) {
332
+ super(`${file} pins no digest for ${apps.join(", ")}. Use the file the same release's \`gkm compose --build --push --digests-file\` wrote, or run without --digests-file to pull by tag.`);
333
+ this.file = file;
334
+ this.apps = apps;
335
+ this.name = "ImageDigestMissing";
336
+ }
337
+ };
338
+ /** A digests file entry that is not this app's image. */
339
+ var ImageDigestMismatch = class extends Error {
340
+ app;
341
+ expected;
342
+ found;
343
+ constructor(app, expected, found) {
344
+ super(`The digests file pins '${app}' to ${found}, but this release runs ${expected}. It was written for another registry, project, stage or tag; use the file this stage's push of this tag wrote.`);
345
+ this.app = app;
346
+ this.expected = expected;
347
+ this.found = found;
348
+ this.name = "ImageDigestMismatch";
349
+ }
350
+ };
351
+ /** A digests file that is not a JSON object of app to pinned ref. */
352
+ var ImageDigestsInvalid = class extends Error {
353
+ file;
354
+ detail;
355
+ constructor(file, detail) {
356
+ super(`${file} is not a digests file (${detail}). It should be the JSON object \`gkm compose --build --push --digests-file\` writes: { "api": "<registry>/<image>:<tag>@sha256:…" }.`);
357
+ this.file = file;
358
+ this.detail = detail;
359
+ this.name = "ImageDigestsInvalid";
360
+ }
361
+ };
362
+ /** Parse a digests file's content, checking its shape. */
363
+ function parseDigests(file, content) {
364
+ let parsed;
365
+ try {
366
+ parsed = JSON.parse(content);
367
+ } catch (error) {
368
+ throw new ImageDigestsInvalid(file, error instanceof Error ? error.message : String(error));
369
+ }
370
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new ImageDigestsInvalid(file, "not a JSON object");
371
+ for (const [app, value] of Object.entries(parsed)) if (typeof value !== "string" || !/@sha256:[0-9a-f]{64}$/.test(value)) throw new ImageDigestsInvalid(file, `'${app}' is not an <image>:<tag>@sha256:… ref`);
372
+ return parsed;
373
+ }
374
+ /**
375
+ * Each image pinned to the digest the file names for it.
376
+ *
377
+ * @throws {ImageDigestMissing} naming every app the file has no entry for
378
+ * @throws {ImageDigestMismatch} when an entry is another image or tag
379
+ */
380
+ function pinImages(file, digests, images) {
381
+ const missing = images.filter((image) => !digests[image.app]).map((image) => image.app);
382
+ if (missing.length > 0) throw new ImageDigestMissing(file, missing);
383
+ return images.map((image) => {
384
+ const pinned = digests[image.app];
385
+ if (pinned.split("@")[0] !== image.ref) throw new ImageDigestMismatch(image.app, image.ref, pinned);
386
+ return {
387
+ ...image,
388
+ ref: pinned
389
+ };
390
+ });
391
+ }
269
392
  //#endregion
270
393
  //#region src/compose/logs.ts
271
394
  /**
@@ -950,6 +1073,7 @@ function composeStack(input) {
950
1073
  if (app.build && Object.keys(args).length > 0) app.build.args = args;
951
1074
  continue;
952
1075
  }
1076
+ if (input.buildOnly) continue;
953
1077
  const keys = require_state.appEnvKeys(manifest, app.name, input.runnables) ?? /* @__PURE__ */ new Set();
954
1078
  const values = valuesFor(app, keys);
955
1079
  const trusted = (0, _geekmidas_manifest.provideKey)(app.id, "trustedOrigins");
@@ -970,7 +1094,7 @@ function composeStack(input) {
970
1094
  };
971
1095
  for (const [key, value] of Object.entries(app.env)) if (/[\r\n]/.test(value)) throw new EnvValueMultiline(app.name, key);
972
1096
  }
973
- for (const worker of workers) {
1097
+ for (const worker of input.buildOnly ? [] : workers) {
974
1098
  const keys = require_state.workerEnvKeys(manifest, worker.id, input.runnables) ?? /* @__PURE__ */ new Set();
975
1099
  worker.env = {
976
1100
  ...valuesFor({
@@ -1003,7 +1127,8 @@ function composeStack(input) {
1003
1127
  http,
1004
1128
  composeDir: (0, node_path.join)(workspace.root, stackDir(stage)),
1005
1129
  buildRoot: layout.buildRoot,
1006
- workspaceRoot: workspace.root
1130
+ workspaceRoot: workspace.root,
1131
+ envFiles: !input.buildOnly
1007
1132
  });
1008
1133
  const caddyfile = edgeCaddyfile([
1009
1134
  ...apps.map((app) => ({
@@ -1023,6 +1148,7 @@ function composeStack(input) {
1023
1148
  dockerfiles[app.build.dockerfile] = require_state.appDockerfile(app.name, workspace.apps[app.name], {
1024
1149
  layout,
1025
1150
  workspaceRoot: workspace.root,
1151
+ apps: workspace.apps,
1026
1152
  manifest,
1027
1153
  cache: STACK_CACHE
1028
1154
  });
@@ -1254,10 +1380,13 @@ function stackFile(options) {
1254
1380
  retries: 5
1255
1381
  }
1256
1382
  };
1257
- for (const service of Object.values(services)) service.logging = {
1258
- driver: LOG_ROTATION.driver,
1259
- options: { ...LOG_ROTATION.options }
1260
- };
1383
+ for (const service of Object.values(services)) {
1384
+ if (!options.envFiles) delete service.env_file;
1385
+ service.logging = {
1386
+ driver: LOG_ROTATION.driver,
1387
+ options: { ...LOG_ROTATION.options }
1388
+ };
1389
+ }
1261
1390
  return {
1262
1391
  name: project,
1263
1392
  services,
@@ -1280,93 +1409,6 @@ function defaultLayout(root) {
1280
1409
  gkmPaths: ["gkm.config.*"]
1281
1410
  };
1282
1411
  }
1283
- /** The id the stack's compose file names an app's build credentials by. */
1284
- function credentialsSecret(app) {
1285
- return `${app}_credentials`;
1286
- }
1287
- /** The file beside the compose file an app's build credentials are read from. */
1288
- function credentialsFile(app) {
1289
- return `${app}.credentials`;
1290
- }
1291
- /**
1292
- * The stack with each backend's image built with its encrypted credentials:
1293
- * a BuildKit secret per app — the file `credentialsFile` names, mounted as
1294
- * `gkm_credentials` — and the hash naming them as a build arg, so a new key
1295
- * rebuilds the layer that embeds them. Each backend's env file gains the
1296
- * `GKM_MASTER_KEY` that decrypts them, the way a Dokploy deploy injects it.
1297
- *
1298
- * Pure: the caller encrypted them.
1299
- */
1300
- function withBuildCredentials(stack, credentials) {
1301
- const services = { ...stack.compose.services };
1302
- const secrets = { ...stack.compose.secrets };
1303
- const apps = stack.apps.map((app) => {
1304
- const own = credentials[app.name];
1305
- const service = services[app.name];
1306
- if (!own || !service?.build) return app;
1307
- const [key, value] = own.buildArg.split("=");
1308
- services[app.name] = {
1309
- ...service,
1310
- build: {
1311
- ...service.build,
1312
- args: {
1313
- ...service.build.args,
1314
- [key]: value
1315
- },
1316
- secrets: [{
1317
- source: credentialsSecret(app.name),
1318
- target: "gkm_credentials"
1319
- }]
1320
- }
1321
- };
1322
- secrets[credentialsSecret(app.name)] = { file: `./${credentialsFile(app.name)}` };
1323
- return {
1324
- ...app,
1325
- env: {
1326
- ...app.env,
1327
- GKM_MASTER_KEY: own.masterKey
1328
- }
1329
- };
1330
- });
1331
- const workers = stack.workers.map((worker) => {
1332
- const own = credentials[worker.name];
1333
- const service = services[worker.name];
1334
- if (!own || !service?.build) return worker;
1335
- const [key, value] = own.buildArg.split("=");
1336
- services[worker.name] = {
1337
- ...service,
1338
- build: {
1339
- ...service.build,
1340
- args: {
1341
- ...service.build.args,
1342
- [key]: value
1343
- },
1344
- secrets: [{
1345
- source: credentialsSecret(worker.name),
1346
- target: "gkm_credentials"
1347
- }]
1348
- }
1349
- };
1350
- secrets[credentialsSecret(worker.name)] = { file: `./${credentialsFile(worker.name)}` };
1351
- return {
1352
- ...worker,
1353
- env: {
1354
- ...worker.env,
1355
- GKM_MASTER_KEY: own.masterKey
1356
- }
1357
- };
1358
- });
1359
- return {
1360
- ...stack,
1361
- apps,
1362
- workers,
1363
- compose: {
1364
- ...stack.compose,
1365
- services,
1366
- ...Object.keys(secrets).length ? { secrets } : {}
1367
- }
1368
- };
1369
- }
1370
1412
  /** Render an app's env file: one `KEY=value` per line, read raw by compose. */
1371
1413
  function envFile(env) {
1372
1414
  return `${Object.entries(env).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${key}=${value}`).join("\n")}\n`;
@@ -1462,6 +1504,20 @@ var ComposeAppsUnhealthy = class extends Error {
1462
1504
  * injected, so the rules are asserted without a daemon; the defaults are the
1463
1505
  * real ones.
1464
1506
  */
1507
+ /** `--push` without `--build`: there is nothing built here to push. */
1508
+ var ComposePushNeedsBuild = class extends Error {
1509
+ constructor() {
1510
+ super("--push pushes the images this checkout builds, so it needs --build (and never --pull): gkm compose --stage <stage> --build --push --tag <tag>.");
1511
+ this.name = "ComposePushNeedsBuild";
1512
+ }
1513
+ };
1514
+ /** Digests to run by, on a run that pulls nothing. */
1515
+ var ComposePinNeedsPull = class extends Error {
1516
+ constructor() {
1517
+ super("--digests-file pins the images a release pulls, so it needs --tag (or --pull) — or, with --build --push, it is where the pushed digests are written.");
1518
+ this.name = "ComposePinNeedsPull";
1519
+ }
1520
+ };
1465
1521
  /** A port variable that does not hold a port. */
1466
1522
  var EdgePortInvalid = class extends Error {
1467
1523
  variable;
@@ -1540,10 +1596,15 @@ const defaultDeps = {
1540
1596
  * every image it names exists. Nothing is written: a missing image stops the
1541
1597
  * run here, before a file, a secret or a container is touched.
1542
1598
  */
1543
- async function validateCompose(ctx, deps, forced) {
1599
+ async function validateCompose(ctx, deps, options = {}) {
1544
1600
  const { workspace, stage, identity, tag } = ctx;
1545
1601
  const root = workspace.root;
1546
- const mode = forced ?? (ctx.tagGiven ? "pull" : "build");
1602
+ const mode = options.mode ?? (ctx.tagGiven ? "pull" : "build");
1603
+ const push = options.push === true;
1604
+ if (push && mode !== "build") throw new ComposePushNeedsBuild();
1605
+ if (options.pin && mode !== "pull") throw new ComposePinNeedsPull();
1606
+ const registry = workspace.deploy?.registry;
1607
+ if (!registry && (push || mode === "pull")) throw new RegistryRequired(push ? "push" : "pull", require_state.imageRef(identity, ctx.apps[0] ?? workspace.name, void 0, tag));
1547
1608
  const runnables = {};
1548
1609
  const background = {};
1549
1610
  const manifest = await require_discover.discover({
@@ -1553,14 +1614,13 @@ async function validateCompose(ctx, deps, forced) {
1553
1614
  background
1554
1615
  });
1555
1616
  const { secrets, generated } = await stageSecrets(ctx, manifest);
1556
- await require_credentialSchemas.assertStageCredentials({
1617
+ if (!push) await require_credentialSchemas.assertStageCredentials({
1557
1618
  root,
1558
1619
  patterns: require_state.constructGlobs(workspace),
1559
1620
  manifest,
1560
1621
  stage,
1561
1622
  supplied: secrets?.custom ?? {}
1562
1623
  });
1563
- const masterKeys = {};
1564
1624
  const layout = mode === "build" ? require_state.imageLayout(workspace) : void 0;
1565
1625
  const composed = composeStack({
1566
1626
  workspace,
@@ -1572,51 +1632,17 @@ async function validateCompose(ctx, deps, forced) {
1572
1632
  images: {
1573
1633
  mode,
1574
1634
  tag,
1575
- ...workspace.deploy?.registry ? { registry: workspace.deploy.registry } : {}
1635
+ ...registry ? { registry } : {}
1576
1636
  },
1577
1637
  secrets,
1578
1638
  allowDevServices: ctx.allowDevServices,
1579
1639
  ports: edgePorts(deps.env),
1580
- ...layout ? { layout } : {}
1640
+ ...layout ? { layout } : {},
1641
+ ...push ? { buildOnly: true } : {}
1581
1642
  });
1582
1643
  require_state.reportDevServices(ctx, composed.devServices);
1583
1644
  if (mode === "build") assertRedisClient(workspace, composed);
1584
- const credentials = {};
1585
- if (mode === "build") {
1586
- for (const app of composed.apps) {
1587
- if (app.kind !== "rest-api" || !app.env || !app.build) continue;
1588
- const { encrypted, iv, masterKey } = require_state.encryptSecrets(app.env);
1589
- credentials[app.name] = {
1590
- encrypted,
1591
- iv
1592
- };
1593
- ctx.secrets.mask(masterKey);
1594
- masterKeys[app.name] = {
1595
- masterKey,
1596
- buildArg: require_state.credentialsBuildArg({
1597
- encrypted,
1598
- iv
1599
- })
1600
- };
1601
- }
1602
- for (const worker of composed.workers) {
1603
- if (!worker.env || !worker.build) continue;
1604
- const { encrypted, iv, masterKey } = require_state.encryptSecrets(worker.env);
1605
- credentials[worker.name] = {
1606
- encrypted,
1607
- iv
1608
- };
1609
- ctx.secrets.mask(masterKey);
1610
- masterKeys[worker.name] = {
1611
- masterKey,
1612
- buildArg: require_state.credentialsBuildArg({
1613
- encrypted,
1614
- iv
1615
- })
1616
- };
1617
- }
1618
- }
1619
- const stack = withBuildCredentials(composed, masterKeys);
1645
+ const stack = options.pin ? withPinnedImages(composed, options.pin.file, options.pin.digests) : composed;
1620
1646
  if (mode === "pull" && !ctx.dryRun) await assertImagesExist(deps.docker, tag, [...stack.apps, ...stack.workers]);
1621
1647
  if (stack.redis && !stack.local) ctx.secrets.mask(stack.redis.password);
1622
1648
  if (stack.logs && (!stack.local || stack.logs.passwordFromSecrets)) {
@@ -1627,7 +1653,8 @@ async function validateCompose(ctx, deps, forced) {
1627
1653
  const override = stackOverrideFile(root, stage);
1628
1654
  const overrides = (0, node_fs.existsSync)(override) ? [override] : [];
1629
1655
  if (overrides.length > 0) ctx.logger.info(`🧩 Merging docker-compose.${stage}.yml over the generated stack`);
1630
- const previous = Object.fromEntries(Object.entries((await ctx.state.read(stage))?.state.releases ?? {}).map(([name, releases]) => {
1656
+ const recorded = push ? void 0 : await ctx.state.read(stage);
1657
+ const previous = Object.fromEntries(Object.entries(recorded?.state.releases ?? {}).map(([name, releases]) => {
1631
1658
  const { ref, tag, digest } = releases.current;
1632
1659
  return [name, {
1633
1660
  ref,
@@ -1637,6 +1664,7 @@ async function validateCompose(ctx, deps, forced) {
1637
1664
  }));
1638
1665
  return {
1639
1666
  mode,
1667
+ push,
1640
1668
  stack,
1641
1669
  dir,
1642
1670
  ref: {
@@ -1651,7 +1679,6 @@ async function validateCompose(ctx, deps, forced) {
1651
1679
  generated,
1652
1680
  previous,
1653
1681
  files: [],
1654
- credentials,
1655
1682
  images: {},
1656
1683
  changes: []
1657
1684
  };
@@ -1713,13 +1740,36 @@ async function stageSecrets(ctx, manifest) {
1713
1740
  };
1714
1741
  }
1715
1742
  /**
1743
+ * The stack with each app's and worker's image pinned to the digest the file
1744
+ * names: the refs it is checked, pulled and recorded by, and the image each
1745
+ * compose service runs.
1746
+ */
1747
+ function withPinnedImages(stack, file, digests) {
1748
+ const apps = pinImages(file, digests, stack.apps);
1749
+ const workers = pinImages(file, digests, stack.workers);
1750
+ const services = { ...stack.compose.services };
1751
+ for (const image of [...apps, ...workers]) services[image.name] = {
1752
+ ...services[image.name],
1753
+ image: image.ref
1754
+ };
1755
+ return {
1756
+ ...stack,
1757
+ apps,
1758
+ workers,
1759
+ compose: {
1760
+ ...stack.compose,
1761
+ services
1762
+ }
1763
+ };
1764
+ }
1765
+ /**
1716
1766
  * A dry run: the stack's files, written so they can be read, and what a real
1717
1767
  * run would do — no image is built or pulled, no container touched, nothing
1718
1768
  * recorded and no secret kept.
1719
1769
  */
1720
1770
  async function planCompose(ctx, run) {
1721
1771
  if (run.generated.length > 0) ctx.logger.info(`🔑 "${ctx.stage}" has no ${run.generated.join(", ")} yet; this dry run used values it did not keep.`);
1722
- run.files = await writeStack(ctx.cwd, run.dir, run.stack, run.credentials);
1772
+ run.files = await writeStack(ctx.cwd, run.dir, run.stack);
1723
1773
  printPlan(ctx, run);
1724
1774
  const planned = (change) => {
1725
1775
  run.changes.push(change);
@@ -1728,7 +1778,7 @@ async function planCompose(ctx, run) {
1728
1778
  ...change
1729
1779
  });
1730
1780
  };
1731
- for (const service of run.stack.infra) planned({
1781
+ for (const service of run.push ? [] : run.stack.infra) planned({
1732
1782
  key: `service:${service}`,
1733
1783
  resourceType: "service",
1734
1784
  action: "ensure"
@@ -1740,6 +1790,7 @@ async function planCompose(ctx, run) {
1740
1790
  action: run.mode === "build" ? "build" : "reuse",
1741
1791
  id: app.ref
1742
1792
  });
1793
+ if (run.push) continue;
1743
1794
  planned({
1744
1795
  key: `service:${app.name}`,
1745
1796
  resourceType: "service",
@@ -1753,13 +1804,14 @@ async function planCompose(ctx, run) {
1753
1804
  action: run.mode === "build" ? "build" : "reuse",
1754
1805
  id: worker.ref
1755
1806
  });
1807
+ if (run.push) continue;
1756
1808
  planned({
1757
1809
  key: `service:${worker.name}`,
1758
1810
  resourceType: "service",
1759
1811
  action: run.previous[worker.name] ? "reuse" : "create"
1760
1812
  });
1761
1813
  }
1762
- planned({
1814
+ if (!run.push) planned({
1763
1815
  key: "service:caddy",
1764
1816
  resourceType: "service",
1765
1817
  action: "ensure"
@@ -1772,11 +1824,12 @@ async function planCompose(ctx, run) {
1772
1824
  */
1773
1825
  async function provisionCompose(ctx, run, deps) {
1774
1826
  const { stack, ref } = run;
1827
+ if (run.push) return;
1775
1828
  if (run.generated.length > 0 && run.secrets) {
1776
1829
  await ctx.secrets.write(run.secrets);
1777
1830
  ctx.logger.info(`🔑 Generated for "${ctx.stage}" (${ctx.secrets.store}): ${run.generated.join(", ")}`);
1778
1831
  }
1779
- run.files = await writeStack(ctx.cwd, run.dir, stack, run.credentials);
1832
+ run.files = await writeStack(ctx.cwd, run.dir, stack);
1780
1833
  printPlan(ctx, run);
1781
1834
  if (stack.infra.length > 0) {
1782
1835
  ctx.logger.info(`\n🗄️ Starting ${stack.infra.join(", ")}…`);
@@ -1844,6 +1897,10 @@ async function buildCompose(ctx, run, deps) {
1844
1897
  const { stack, ref } = run;
1845
1898
  const images = [...stack.apps, ...stack.workers];
1846
1899
  const apps = images.map((image) => image.app);
1900
+ if (run.push) {
1901
+ run.files = await writeStack(ctx.cwd, run.dir, stack);
1902
+ printPlan(ctx, run);
1903
+ }
1847
1904
  if (run.mode === "build") {
1848
1905
  ctx.logger.info("\n🐳 Building images…");
1849
1906
  await deps.docker.build(ref, apps);
@@ -1851,8 +1908,10 @@ async function buildCompose(ctx, run, deps) {
1851
1908
  ctx.logger.info(`\n🐳 Pulling ${ctx.tag}…`);
1852
1909
  await deps.docker.pull(ref, apps);
1853
1910
  }
1911
+ if (run.push) ctx.logger.info("\n📤 Pushing images…");
1854
1912
  for (const image of images) {
1855
- const digest = await deps.docker.digest(image.ref);
1913
+ const digest = run.push ? await deps.docker.push(ref, image.ref) : await deps.docker.digest(image.ref);
1914
+ if (run.push) ctx.logger.info(` ${image.app.padEnd(12)} ${image.ref}@${digest}`);
1856
1915
  run.images[image.app] = {
1857
1916
  ref: image.ref,
1858
1917
  tag: image.tag,
@@ -1878,6 +1937,7 @@ async function buildCompose(ctx, run, deps) {
1878
1937
  */
1879
1938
  async function releaseCompose(ctx, run, deps) {
1880
1939
  const { stack, ref } = run;
1940
+ if (run.push) return;
1881
1941
  ctx.logger.info("\n🚀 Starting the stack…");
1882
1942
  await deps.docker.up(ref);
1883
1943
  if (stack.local) await deps.docker.copyOut(ref, "caddy", "/data/caddy/pki/authorities/local/root.crt", caFile(run)).catch(() => {});
@@ -1936,6 +1996,7 @@ async function recordImages(ctx, run) {
1936
1996
  */
1937
1997
  async function verifyCompose(ctx, run, deps) {
1938
1998
  const { stack } = run;
1999
+ if (run.push) return;
1939
2000
  const ca = stack.local ? await (0, node_fs_promises.readFile)(caFile(run), "utf-8").catch(() => void 0) : void 0;
1940
2001
  ctx.logger.info("\n🩺 Checking each app through Caddy…");
1941
2002
  const results = await Promise.all(stack.apps.map((app) => checkApp(ctx, app, deps, {
@@ -2115,7 +2176,7 @@ function applied(ctx, run, change) {
2115
2176
  });
2116
2177
  }
2117
2178
  /** Write the stack's files: owner-only, since the env files hold secrets. */
2118
- async function writeStack(root, dir, stack, credentials = {}) {
2179
+ async function writeStack(root, dir, stack) {
2119
2180
  await (0, node_fs_promises.mkdir)(dir, {
2120
2181
  recursive: true,
2121
2182
  mode: 448
@@ -2131,9 +2192,9 @@ async function writeStack(root, dir, stack, credentials = {}) {
2131
2192
  await write((0, node_path.join)(dir, "docker-compose.yml"), composeYaml(stack));
2132
2193
  await write((0, node_path.join)(dir, "Caddyfile"), stack.caddyfile);
2133
2194
  for (const app of [...stack.apps, ...stack.workers]) if (app.env) await write((0, node_path.join)(dir, `${app.name}.env`), envFile(app.env), 384);
2134
- if (stack.logs) await write((0, node_path.join)(dir, `${LOGS_SERVICE}.env`), envFile(stack.logs.env), 384);
2135
- if (stack.redis) await write((0, node_path.join)(dir, `${REDIS_SERVICE}.env`), envFile(stack.redis.env), 384);
2136
- for (const [name, payload] of Object.entries(credentials)) await write((0, node_path.join)(dir, credentialsFile(name)), require_state.credentialsFileContent(payload), 384);
2195
+ if (stack.logs && stack.compose.services["openobserve"]?.env_file) await write((0, node_path.join)(dir, `${LOGS_SERVICE}.env`), envFile(stack.logs.env), 384);
2196
+ if (stack.redis && stack.compose.services["redis"]?.env_file) await write((0, node_path.join)(dir, `${REDIS_SERVICE}.env`), envFile(stack.redis.env), 384);
2197
+ for (const entry of await (0, node_fs_promises.readdir)(dir)) if (entry.endsWith(".credentials")) await (0, node_fs_promises.rm)((0, node_path.join)(dir, entry));
2137
2198
  for (const [path, content] of Object.entries(stack.dockerfiles)) await write((0, node_path.join)(root, path), content);
2138
2199
  await require_state.ensureDockerignore(require_state.findBuildRoot(root));
2139
2200
  return files;
@@ -2183,13 +2244,16 @@ function printPlan(ctx, run) {
2183
2244
  * digest in the stage's state
2184
2245
  * - `verify`: each app asked through Caddy over HTTPS
2185
2246
  *
2247
+ * With `push` (`gkm compose --build --push`), run with `buildOnly`, it builds
2248
+ * every image and pushes it to `deploy.registry`, and does nothing else.
2249
+ *
2186
2250
  * `gkm compose` is this target with a few more switches (`--build`, `--pull`,
2187
2251
  * `--down`), run through the same `deploy()`.
2188
2252
  */
2189
2253
  var compose_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({ composeTarget: () => composeTarget });
2190
2254
  /** The compose target, with its Docker, bundler and probe as given. */
2191
2255
  function composeTarget(options = {}) {
2192
- const { mode, report, ...given } = options;
2256
+ const { mode, push, pin, report, ...given } = options;
2193
2257
  const deps = {
2194
2258
  ...defaultDeps,
2195
2259
  ...given
@@ -2205,7 +2269,11 @@ function composeTarget(options = {}) {
2205
2269
  },
2206
2270
  credentials: [],
2207
2271
  tag: ({ cwd }) => deps.revision(cwd),
2208
- validate: (ctx) => validateCompose(ctx, deps, mode),
2272
+ validate: (ctx) => validateCompose(ctx, deps, {
2273
+ ...mode ? { mode } : {},
2274
+ ...push ? { push } : {},
2275
+ ...pin ? { pin } : {}
2276
+ }),
2209
2277
  plan: (ctx, run) => planCompose(ctx, run),
2210
2278
  provision: (ctx, run) => provisionCompose(ctx, run, deps),
2211
2279
  build: (ctx, run) => buildCompose(ctx, run, deps),
@@ -2218,6 +2286,18 @@ function composeTarget(options = {}) {
2218
2286
  });
2219
2287
  }
2220
2288
  //#endregion
2289
+ Object.defineProperty(exports, "ComposePinNeedsPull", {
2290
+ enumerable: true,
2291
+ get: function() {
2292
+ return ComposePinNeedsPull;
2293
+ }
2294
+ });
2295
+ Object.defineProperty(exports, "ComposePushNeedsBuild", {
2296
+ enumerable: true,
2297
+ get: function() {
2298
+ return ComposePushNeedsBuild;
2299
+ }
2300
+ });
2221
2301
  Object.defineProperty(exports, "composeProject", {
2222
2302
  enumerable: true,
2223
2303
  get: function() {
@@ -2242,6 +2322,18 @@ Object.defineProperty(exports, "dockerCompose", {
2242
2322
  return dockerCompose;
2243
2323
  }
2244
2324
  });
2325
+ Object.defineProperty(exports, "parseDigests", {
2326
+ enumerable: true,
2327
+ get: function() {
2328
+ return parseDigests;
2329
+ }
2330
+ });
2331
+ Object.defineProperty(exports, "pinnedRef", {
2332
+ enumerable: true,
2333
+ get: function() {
2334
+ return pinnedRef;
2335
+ }
2336
+ });
2245
2337
  Object.defineProperty(exports, "stackDir", {
2246
2338
  enumerable: true,
2247
2339
  get: function() {
@@ -2255,4 +2347,4 @@ Object.defineProperty(exports, "stackOverrideFile", {
2255
2347
  }
2256
2348
  });
2257
2349
 
2258
- //# sourceMappingURL=compose-iuw8wfHk.cjs.map
2350
+ //# sourceMappingURL=compose-h_6oNnHC.cjs.map