@geekmidas/cli 10.0.0-alpha.77 → 10.0.0-alpha.79
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.
- package/dist/{LegacyStateStore-dyq07_Ir.mjs → LegacyStateStore-Ceq7A_75.mjs} +2 -2
- package/dist/{LegacyStateStore-dyq07_Ir.mjs.map → LegacyStateStore-Ceq7A_75.mjs.map} +1 -1
- package/dist/{LegacyStateStore-DwngfJxr.cjs → LegacyStateStore-Z-EKUGSX.cjs} +2 -2
- package/dist/{LegacyStateStore-DwngfJxr.cjs.map → LegacyStateStore-Z-EKUGSX.cjs.map} +1 -1
- package/dist/{LocalStateStore-B0NCde8Z.cjs → LocalStateStore-Bocb8dmZ.cjs} +2 -2
- package/dist/{LocalStateStore-B0NCde8Z.cjs.map → LocalStateStore-Bocb8dmZ.cjs.map} +1 -1
- package/dist/{LocalStateStore-DLxCCatI.mjs → LocalStateStore-C72bwpG2.mjs} +2 -2
- package/dist/{LocalStateStore-DLxCCatI.mjs.map → LocalStateStore-C72bwpG2.mjs.map} +1 -1
- package/dist/{S3StateStore-BOenb-3o.cjs → S3StateStore-35YfcdL_.cjs} +2 -2
- package/dist/{S3StateStore-BOenb-3o.cjs.map → S3StateStore-35YfcdL_.cjs.map} +1 -1
- package/dist/{S3StateStore-CGK_4hlo.mjs → S3StateStore-C9upSW5o.mjs} +2 -2
- package/dist/{S3StateStore-CGK_4hlo.mjs.map → S3StateStore-C9upSW5o.mjs.map} +1 -1
- package/dist/{SSMStateStore-DYeD__7p.cjs → SSMStateStore-W-Ps22AQ.cjs} +2 -2
- package/dist/{SSMStateStore-DYeD__7p.cjs.map → SSMStateStore-W-Ps22AQ.cjs.map} +1 -1
- package/dist/{SSMStateStore-B_5_RqY-.mjs → SSMStateStore-dOmFO6g1.mjs} +2 -2
- package/dist/{SSMStateStore-B_5_RqY-.mjs.map → SSMStateStore-dOmFO6g1.mjs.map} +1 -1
- package/dist/{add-DCsgtvDk.cjs → add-HF9POevk.cjs} +8 -8
- package/dist/{add-DCsgtvDk.cjs.map → add-HF9POevk.cjs.map} +1 -1
- package/dist/{add-EPQO-_dt.mjs → add-UFiIrlTj.mjs} +3 -3
- package/dist/{add-EPQO-_dt.mjs.map → add-UFiIrlTj.mjs.map} +1 -1
- package/dist/{bundler-DgO_EMVs.mjs → bundler-C45BuRdY.mjs} +5 -5
- package/dist/{bundler-DgO_EMVs.mjs.map → bundler-C45BuRdY.mjs.map} +1 -1
- package/dist/{bundler-5spPyduN.cjs → bundler-RAoSRuvp.cjs} +5 -5
- package/dist/{bundler-5spPyduN.cjs.map → bundler-RAoSRuvp.cjs.map} +1 -1
- package/dist/{cli-ZpFTMbGw.cjs → cli-C3hsEwLK.cjs} +4 -4
- package/dist/{cli-ZpFTMbGw.cjs.map → cli-C3hsEwLK.cjs.map} +1 -1
- package/dist/{cli-B74Ru-V1.mjs → cli-gvc3urUl.mjs} +3 -3
- package/dist/{cli-B74Ru-V1.mjs.map → cli-gvc3urUl.mjs.map} +1 -1
- package/dist/{compose-IUuxRW0J.mjs → compose-C40Wk9m6.mjs} +626 -65
- package/dist/compose-C40Wk9m6.mjs.map +1 -0
- package/dist/{compose-CePlrbUw.cjs → compose-CtUP07r3.cjs} +690 -111
- package/dist/compose-CtUP07r3.cjs.map +1 -0
- package/dist/{config-Ckmoj56i.d.mts → config-L_6NivCp.d.mts} +3 -3
- package/dist/{config-Ckmoj56i.d.mts.map → config-L_6NivCp.d.mts.map} +1 -1
- package/dist/{config-aAeLXH86.d.cts → config-Mr4rcXFC.d.cts} +3 -3
- package/dist/{config-aAeLXH86.d.cts.map → config-Mr4rcXFC.d.cts.map} +1 -1
- package/dist/config.cjs +1 -1
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +1 -1
- package/dist/{databases-C2p5LGAp.cjs → databases-BY-pEImp.cjs} +185 -7
- package/dist/databases-BY-pEImp.cjs.map +1 -0
- package/dist/{databases-hUHTuwhx.mjs → databases-CzszQV0N.mjs} +153 -5
- package/dist/databases-CzszQV0N.mjs.map +1 -0
- package/dist/deploy/public.cjs +13 -12
- package/dist/deploy/public.d.cts +19 -3
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +19 -3
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +7 -7
- package/dist/{deploy-Q6X6RXRG.mjs → deploy-BjsZpUQ3.mjs} +5 -5
- package/dist/{deploy-Q6X6RXRG.mjs.map → deploy-BjsZpUQ3.mjs.map} +1 -1
- package/dist/{deploy-XHXi-_tL.cjs → deploy-DPzauQsm.cjs} +11 -11
- package/dist/{deploy-XHXi-_tL.cjs.map → deploy-DPzauQsm.cjs.map} +1 -1
- package/dist/{dokploy-BtP5XSrS.mjs → dokploy-BoH0DMyS.mjs} +2 -2
- package/dist/{dokploy-BtP5XSrS.mjs.map → dokploy-BoH0DMyS.mjs.map} +1 -1
- package/dist/{dokploy-MSsPBO-t.cjs → dokploy-Cj1EHd4Y.cjs} +2 -2
- package/dist/{dokploy-MSsPBO-t.cjs.map → dokploy-Cj1EHd4Y.cjs.map} +1 -1
- package/dist/{encryption-DY0na43R.cjs → encryption-D2z16vsS.cjs} +6 -6
- package/dist/{encryption-DY0na43R.cjs.map → encryption-D2z16vsS.cjs.map} +1 -1
- package/dist/{encryption-D4Ykywfr.mjs → encryption-MeDrE-rZ.mjs} +3 -3
- package/dist/{encryption-D4Ykywfr.mjs.map → encryption-MeDrE-rZ.mjs.map} +1 -1
- package/dist/{engine-BI2xjCKt.mjs → engine-B-Yq0yQu.mjs} +55 -19
- package/dist/engine-B-Yq0yQu.mjs.map +1 -0
- package/dist/{engine-y53kE_3H.cjs → engine-C3T39eTO.cjs} +120 -84
- package/dist/engine-C3T39eTO.cjs.map +1 -0
- package/dist/{github-Bmb45xUQ.cjs → github-B8dDa8xf.cjs} +7 -7
- package/dist/{github-Bmb45xUQ.cjs.map → github-B8dDa8xf.cjs.map} +1 -1
- package/dist/{github-BBIqk1Jv.mjs → github-Dyrjo0xd.mjs} +3 -3
- package/dist/{github-BBIqk1Jv.mjs.map → github-Dyrjo0xd.mjs.map} +1 -1
- package/dist/{index-CNdSRwar.d.mts → index-C0UmP_lg.d.mts} +2 -2
- package/dist/{index-C7Tma2m1.d.cts.map → index-C0UmP_lg.d.mts.map} +1 -1
- package/dist/{index-C7Tma2m1.d.cts → index-DTEPwLml.d.cts} +2 -2
- package/dist/{index-CNdSRwar.d.mts.map → index-DTEPwLml.d.cts.map} +1 -1
- package/dist/index.cjs +119 -118
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +46 -45
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-BUBsUiM3.mjs → migrate-A2CLFl9G.mjs} +4 -5
- package/dist/{migrate-BUBsUiM3.mjs.map → migrate-A2CLFl9G.mjs.map} +1 -1
- package/dist/{migrate-sny0NjKe.cjs → migrate-CTqICj1B.cjs} +9 -10
- package/dist/{migrate-sny0NjKe.cjs.map → migrate-CTqICj1B.cjs.map} +1 -1
- package/dist/{migrate-DWVl5PQK.mjs → migrate-DWUbYY5r.mjs} +3 -3
- package/dist/{migrate-DWVl5PQK.mjs.map → migrate-DWUbYY5r.mjs.map} +1 -1
- package/dist/{migrate-31JXUOaG.cjs → migrate-DvIAITiG.cjs} +5 -5
- package/dist/{migrate-31JXUOaG.cjs.map → migrate-DvIAITiG.cjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/{resolve-BF1ICosS.mjs → resolve-CHwetPdo.mjs} +3 -3
- package/dist/{resolve-BF1ICosS.mjs.map → resolve-CHwetPdo.mjs.map} +1 -1
- package/dist/{resolve-DokLtTUb.cjs → resolve-DOXcAOcn.cjs} +3 -3
- package/dist/{resolve-DokLtTUb.cjs.map → resolve-DOXcAOcn.cjs.map} +1 -1
- package/dist/{rollback-sg0ZhKGG.mjs → rollback-Cq5s3WnD.mjs} +5 -5
- package/dist/{rollback-sg0ZhKGG.mjs.map → rollback-Cq5s3WnD.mjs.map} +1 -1
- package/dist/{rollback-CpOWRR9H.cjs → rollback-DUoOaws3.cjs} +7 -7
- package/dist/{rollback-CpOWRR9H.cjs.map → rollback-DUoOaws3.cjs.map} +1 -1
- package/dist/{runtime-BozOkPtG.d.cts → runtime-D6A-C6zh.d.cts} +2 -2
- package/dist/{runtime-B5bYom9w.d.mts.map → runtime-D6A-C6zh.d.cts.map} +1 -1
- package/dist/{runtime-B5bYom9w.d.mts → runtime-ekxfPNM-.d.mts} +2 -2
- package/dist/{runtime-BozOkPtG.d.cts.map → runtime-ekxfPNM-.d.mts.map} +1 -1
- package/dist/sandbox/migrate-worker.cjs +22 -8
- package/dist/sandbox/migrate-worker.cjs.map +1 -1
- package/dist/sandbox/migrate-worker.mjs +22 -8
- package/dist/sandbox/migrate-worker.mjs.map +1 -1
- package/dist/{stale-DGzpiN5S.mjs → seeds-DiXON7R8.mjs} +64 -6
- package/dist/seeds-DiXON7R8.mjs.map +1 -0
- package/dist/{stale-yu2FkH3G.cjs → seeds-qX13ubmO.cjs} +81 -5
- package/dist/seeds-qX13ubmO.cjs.map +1 -0
- package/dist/{sniffer-Y6GQOCXB.cjs → sniffer-BjG-1mlD.cjs} +6 -6
- package/dist/sniffer-BjG-1mlD.cjs.map +1 -0
- package/dist/{sniffer-DR8AlRCk.mjs → sniffer-ByWtH28i.mjs} +6 -6
- package/dist/sniffer-ByWtH28i.mjs.map +1 -0
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.d.cts +2 -2
- package/dist/target/public.d.mts +2 -2
- package/dist/target/public.mjs +1 -1
- package/dist/{types-ct7pdQRf.d.cts → types-BzCavjGt.d.mts} +70 -3
- package/dist/types-BzCavjGt.d.mts.map +1 -0
- package/dist/{types-B9yWJAis.d.mts → types-DZtj9jeI.d.cts} +71 -2
- package/dist/types-DZtj9jeI.d.cts.map +1 -0
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-QUTqYmVy.mjs → workspace-BBdqmkch.mjs} +114 -17
- package/dist/workspace-BBdqmkch.mjs.map +1 -0
- package/dist/{workspace-DOm81oUo.cjs → workspace-Bp8FEkF-.cjs} +130 -15
- package/dist/workspace-Bp8FEkF-.cjs.map +1 -0
- package/package.json +15 -15
- package/dist/compose-CePlrbUw.cjs.map +0 -1
- package/dist/compose-IUuxRW0J.mjs.map +0 -1
- package/dist/databases-C2p5LGAp.cjs.map +0 -1
- package/dist/databases-hUHTuwhx.mjs.map +0 -1
- package/dist/engine-BI2xjCKt.mjs.map +0 -1
- package/dist/engine-y53kE_3H.cjs.map +0 -1
- package/dist/provider-B3Sv_a74.cjs +0 -173
- package/dist/provider-B3Sv_a74.cjs.map +0 -1
- package/dist/provider-D7rQzzzf.mjs +0 -138
- package/dist/provider-D7rQzzzf.mjs.map +0 -1
- package/dist/sniffer-DR8AlRCk.mjs.map +0 -1
- package/dist/sniffer-Y6GQOCXB.cjs.map +0 -1
- package/dist/stale-DGzpiN5S.mjs.map +0 -1
- package/dist/stale-yu2FkH3G.cjs.map +0 -1
- package/dist/types-B9yWJAis.d.mts.map +0 -1
- package/dist/types-ct7pdQRf.d.cts.map +0 -1
- package/dist/workspace-DOm81oUo.cjs.map +0 -1
- package/dist/workspace-QUTqYmVy.mjs.map +0 -1
|
@@ -2,12 +2,12 @@ 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-
|
|
5
|
+
const require_workspace = require("./workspace-Bp8FEkF-.cjs");
|
|
6
6
|
const require_define = require("./define-DzRz8FgH.cjs");
|
|
7
|
-
const
|
|
7
|
+
const require_seeds = require("./seeds-qX13ubmO.cjs");
|
|
8
8
|
const require_SubscriberGenerator = require("./SubscriberGenerator-CajwOa5W.cjs");
|
|
9
9
|
const require_drivers = require("./drivers-BdvtCKc7.cjs");
|
|
10
|
-
const require_databases = require("./databases-
|
|
10
|
+
const require_databases = require("./databases-BY-pEImp.cjs");
|
|
11
11
|
const require_credentialSchemas = require("./credentialSchemas-DCR4Wd1W.cjs");
|
|
12
12
|
let node_path = require("node:path");
|
|
13
13
|
let node_fs = require("node:fs");
|
|
@@ -49,6 +49,27 @@ function runOptions(stack, env) {
|
|
|
49
49
|
...stack.signal ? { signal: stack.signal } : {}
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The host ports a `docker ps` Ports column publishes: `0.0.0.0:443->443/tcp,
|
|
54
|
+
* [::]:443->443/tcp, 0.0.0.0:9000-9001->9000-9001/tcp`.
|
|
55
|
+
*/
|
|
56
|
+
function publishedPorts(column) {
|
|
57
|
+
const ports = /* @__PURE__ */ new Set();
|
|
58
|
+
for (const match of column.matchAll(/(?:[\d.]+|\[[^\]]*\]):(\d+)(?:-(\d+))?->/g)) {
|
|
59
|
+
const first = Number(match[1]);
|
|
60
|
+
const last = match[2] ? Number(match[2]) : first;
|
|
61
|
+
for (let port = first; port <= last; port++) ports.add(port);
|
|
62
|
+
}
|
|
63
|
+
return [...ports];
|
|
64
|
+
}
|
|
65
|
+
/** Each line `docker ps` printed, as the containers holding `port`. */
|
|
66
|
+
function portHolders(output, port) {
|
|
67
|
+
return output.split("\n").filter(Boolean).map((line) => line.split(" ")).filter(([, , , column = ""]) => publishedPorts(column).includes(port)).map(([container = "", project, service]) => ({
|
|
68
|
+
container,
|
|
69
|
+
...project ? { project } : {},
|
|
70
|
+
...service ? { service } : {}
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
52
73
|
/** How long a stack has to pass its health checks. */
|
|
53
74
|
const WAIT_TIMEOUT_S = 180;
|
|
54
75
|
/** `docker compose port` printed nothing a port can be read from. */
|
|
@@ -249,8 +270,389 @@ const dockerCompose = {
|
|
|
249
270
|
if (match) return match.slice(repository.length + 1);
|
|
250
271
|
} catch {}
|
|
251
272
|
return id || void 0;
|
|
273
|
+
},
|
|
274
|
+
async ensureNetwork(name) {
|
|
275
|
+
const { code } = await capture("docker", [
|
|
276
|
+
"network",
|
|
277
|
+
"inspect",
|
|
278
|
+
name
|
|
279
|
+
]);
|
|
280
|
+
if (code === 0) return;
|
|
281
|
+
const created = await capture("docker", [
|
|
282
|
+
"network",
|
|
283
|
+
"create",
|
|
284
|
+
name
|
|
285
|
+
]);
|
|
286
|
+
if (created.code !== 0 && !/already exists/.test(created.stderr)) throw new NetworkCreateFailed(name, created.stderr);
|
|
287
|
+
},
|
|
288
|
+
async publishers(port) {
|
|
289
|
+
const { stdout } = await capture("docker", [
|
|
290
|
+
"ps",
|
|
291
|
+
"--format",
|
|
292
|
+
"{{.Names}} {{.Label \"com.docker.compose.project\"}} {{.Label \"com.docker.compose.service\"}} {{.Ports}}"
|
|
293
|
+
]);
|
|
294
|
+
return portHolders(stdout, port);
|
|
252
295
|
}
|
|
253
296
|
};
|
|
297
|
+
/** `docker network create` failed. */
|
|
298
|
+
var NetworkCreateFailed = class extends Error {
|
|
299
|
+
network;
|
|
300
|
+
output;
|
|
301
|
+
constructor(network, output) {
|
|
302
|
+
super(`Could not create the Docker network '${network}' (docker said: ${output.trim() || "nothing"}). Create it by hand with \`docker network create ${network}\` and run again.`);
|
|
303
|
+
this.network = network;
|
|
304
|
+
this.output = output;
|
|
305
|
+
this.name = "NetworkCreateFailed";
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/compose/traefik.ts
|
|
310
|
+
/**
|
|
311
|
+
* The server's shared edge, rendered for Traefik: what the edge itself runs
|
|
312
|
+
* with, and what one stack registers with it.
|
|
313
|
+
*
|
|
314
|
+
* One Traefik per server owns 80 and 443, the ACME state and the redirect to
|
|
315
|
+
* HTTPS. It is configured through its file provider alone: each stack writes
|
|
316
|
+
* one file — its routers, services and middlewares, every name prefixed with
|
|
317
|
+
* its compose project — into a directory the edge watches, and removes it
|
|
318
|
+
* when it stops. There is no Docker socket: the edge never learns what runs
|
|
319
|
+
* on the server except from those files.
|
|
320
|
+
*
|
|
321
|
+
* The edge and the stacks meet on one external Docker network. Only a
|
|
322
|
+
* stack's public services join it, each under an alias prefixed with its
|
|
323
|
+
* project (`<project>-api`), because the network is shared: a bare service
|
|
324
|
+
* name there would be answered by every stack's `api`.
|
|
325
|
+
*
|
|
326
|
+
* Pure: the documents are data, and the edge's side effects are `edge.ts`'s.
|
|
327
|
+
*/
|
|
328
|
+
/**
|
|
329
|
+
* The image, pinned: the edge is every stack's way in, so a new version is a
|
|
330
|
+
* change to this line, not whatever `latest` is on the day a server starts.
|
|
331
|
+
*/
|
|
332
|
+
const TRAEFIK_IMAGE = "traefik:v3.7.13";
|
|
333
|
+
/** The edge's compose project, one per server. */
|
|
334
|
+
const EDGE_PROJECT = "gkm-edge";
|
|
335
|
+
/** The external network the edge and every stack's public services share. */
|
|
336
|
+
const EDGE_NETWORK = "gkm-edge";
|
|
337
|
+
/** The edge's compose service. */
|
|
338
|
+
const EDGE_SERVICE = "traefik";
|
|
339
|
+
/** Inside the edge's container: what it watches, and where certificates are. */
|
|
340
|
+
const EDGE_DYNAMIC_DIR = "/etc/gkm-edge/dynamic";
|
|
341
|
+
const EDGE_CERTS_DIR = "/etc/gkm-edge/certs";
|
|
342
|
+
/** The certificate resolver every deployed route without its own certificate uses. */
|
|
343
|
+
const ACME_RESOLVER = "letsencrypt";
|
|
344
|
+
/** The internal entrypoint the edge's health check asks; never published. */
|
|
345
|
+
const PING_PORT = 8082;
|
|
346
|
+
/** A stack's service, as the edge — and the stack itself — reach it. */
|
|
347
|
+
function edgeAlias(project, service) {
|
|
348
|
+
return `${project}-${service}`;
|
|
349
|
+
}
|
|
350
|
+
/** A stack's certificate, as the edge's container reads it. */
|
|
351
|
+
function edgeCertificatePaths(project) {
|
|
352
|
+
return {
|
|
353
|
+
certFile: `${EDGE_CERTS_DIR}/${project}.crt`,
|
|
354
|
+
keyFile: `${EDGE_CERTS_DIR}/${project}.key`
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* The edge's static configuration: the entrypoints, the redirect, ACME, and
|
|
359
|
+
* the directory it watches. No API and no dashboard — nothing about the edge
|
|
360
|
+
* is reachable but the routes stacks register.
|
|
361
|
+
*/
|
|
362
|
+
function traefikStatic(ports) {
|
|
363
|
+
return {
|
|
364
|
+
global: {
|
|
365
|
+
checkNewVersion: false,
|
|
366
|
+
sendAnonymousUsage: false
|
|
367
|
+
},
|
|
368
|
+
entryPoints: {
|
|
369
|
+
web: {
|
|
370
|
+
address: ":80",
|
|
371
|
+
http: { redirections: { entryPoint: {
|
|
372
|
+
to: ports.https === 443 ? "websecure" : `:${ports.https}`,
|
|
373
|
+
scheme: "https",
|
|
374
|
+
permanent: true
|
|
375
|
+
} } }
|
|
376
|
+
},
|
|
377
|
+
websecure: { address: ":443" },
|
|
378
|
+
ping: { address: `:${PING_PORT}` }
|
|
379
|
+
},
|
|
380
|
+
ping: { entryPoint: "ping" },
|
|
381
|
+
providers: { file: {
|
|
382
|
+
directory: EDGE_DYNAMIC_DIR,
|
|
383
|
+
watch: true
|
|
384
|
+
} },
|
|
385
|
+
certificatesResolvers: { [ACME_RESOLVER]: { acme: {
|
|
386
|
+
storage: "/acme/acme.json",
|
|
387
|
+
httpChallenge: { entryPoint: "web" }
|
|
388
|
+
} } },
|
|
389
|
+
log: { level: "INFO" }
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
/** The edge's compose document. */
|
|
393
|
+
function edgeCompose(options) {
|
|
394
|
+
const { dir, ports } = options;
|
|
395
|
+
return {
|
|
396
|
+
name: EDGE_PROJECT,
|
|
397
|
+
services: { [EDGE_SERVICE]: {
|
|
398
|
+
image: TRAEFIK_IMAGE,
|
|
399
|
+
restart: "unless-stopped",
|
|
400
|
+
ports: [`${ports.https}:443`, `${ports.http}:80`],
|
|
401
|
+
volumes: [
|
|
402
|
+
`${dir}/traefik.yml:/etc/traefik/traefik.yml:ro`,
|
|
403
|
+
`${dir}/dynamic:${EDGE_DYNAMIC_DIR}:ro`,
|
|
404
|
+
`${dir}/certs:${EDGE_CERTS_DIR}:ro`,
|
|
405
|
+
"acme:/acme"
|
|
406
|
+
],
|
|
407
|
+
labels: { "dev.geekmidas.edge.config": (0, node_crypto.createHash)("sha256").update(options.staticConfig).digest("hex").slice(0, 16) },
|
|
408
|
+
networks: ["edge"],
|
|
409
|
+
healthcheck: {
|
|
410
|
+
test: [
|
|
411
|
+
"CMD",
|
|
412
|
+
"traefik",
|
|
413
|
+
"healthcheck",
|
|
414
|
+
"--ping"
|
|
415
|
+
],
|
|
416
|
+
interval: "10s",
|
|
417
|
+
timeout: "5s",
|
|
418
|
+
retries: 5
|
|
419
|
+
},
|
|
420
|
+
logging: options.logging
|
|
421
|
+
} },
|
|
422
|
+
volumes: { acme: {} },
|
|
423
|
+
networks: { edge: {
|
|
424
|
+
name: EDGE_NETWORK,
|
|
425
|
+
external: true
|
|
426
|
+
} }
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
/** A stack's dynamic configuration, as an object. */
|
|
430
|
+
function traefikDynamic(routes, options) {
|
|
431
|
+
const { project, tls } = options;
|
|
432
|
+
const name = (route, suffix) => `${project}-${route.name}${suffix ? `-${suffix}` : ""}`;
|
|
433
|
+
const url = ({ service, port }) => `http://${edgeAlias(project, service)}:${port}`;
|
|
434
|
+
const routers = {};
|
|
435
|
+
const services = {};
|
|
436
|
+
const middlewares = {};
|
|
437
|
+
for (const route of routes) {
|
|
438
|
+
const chain = [];
|
|
439
|
+
if (route.allow) {
|
|
440
|
+
middlewares[name(route, "allow")] = { ipAllowList: { sourceRange: [...route.allow] } };
|
|
441
|
+
chain.push(name(route, "allow"));
|
|
442
|
+
}
|
|
443
|
+
if (route.prefix) {
|
|
444
|
+
middlewares[name(route, "prefix")] = { addPrefix: { prefix: route.prefix } };
|
|
445
|
+
chain.push(name(route, "prefix"));
|
|
446
|
+
}
|
|
447
|
+
routers[name(route)] = {
|
|
448
|
+
rule: `Host(\`${route.host}\`)`,
|
|
449
|
+
entryPoints: ["websecure"],
|
|
450
|
+
service: name(route),
|
|
451
|
+
...chain.length > 0 ? { middlewares: chain } : {},
|
|
452
|
+
tls: tls.kind === "acme" ? { certResolver: ACME_RESOLVER } : {}
|
|
453
|
+
};
|
|
454
|
+
services[name(route)] = { loadBalancer: {
|
|
455
|
+
servers: route.upstreams.map((upstream) => ({ url: url(upstream) })),
|
|
456
|
+
...route.prefix ? { passHostHeader: false } : {},
|
|
457
|
+
...route.streaming ? { responseForwarding: { flushInterval: "-1ms" } } : {},
|
|
458
|
+
...route.health ? { healthCheck: {
|
|
459
|
+
path: route.health,
|
|
460
|
+
interval: "5s",
|
|
461
|
+
timeout: "3s"
|
|
462
|
+
} } : {}
|
|
463
|
+
} };
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
http: {
|
|
467
|
+
routers,
|
|
468
|
+
services,
|
|
469
|
+
...Object.keys(middlewares).length > 0 ? { middlewares } : {}
|
|
470
|
+
},
|
|
471
|
+
...tls.kind === "files" ? { tls: { certificates: [{
|
|
472
|
+
certFile: tls.certFile,
|
|
473
|
+
keyFile: tls.keyFile
|
|
474
|
+
}] } } : {}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
/** A stack's dynamic configuration file, as written to the edge's directory. */
|
|
478
|
+
function traefikDynamicFile(routes, options) {
|
|
479
|
+
return `# Generated by gkm compose for ${options.project} — do not edit.
|
|
480
|
+
# The shared edge (${EDGE_PROJECT}) reads every file in this directory; this
|
|
481
|
+
# one is removed by gkm compose --down.
|
|
482
|
+
${yaml$1(traefikDynamic(routes, options))}`;
|
|
483
|
+
}
|
|
484
|
+
/** The edge's static configuration file. */
|
|
485
|
+
function traefikStaticFile(ports) {
|
|
486
|
+
return `# Generated by gkm compose — the server's shared edge. Do not edit.
|
|
487
|
+
${yaml$1(traefikStatic(ports))}`;
|
|
488
|
+
}
|
|
489
|
+
/** The edge's compose file. */
|
|
490
|
+
function edgeComposeFile(options) {
|
|
491
|
+
return `# Generated by gkm compose — the server's shared edge. Do not edit.
|
|
492
|
+
#
|
|
493
|
+
# Started by the first stack that runs with proxy: 'traefik', and left
|
|
494
|
+
# running by gkm compose --down. Stop it, once nothing uses it, with:
|
|
495
|
+
#
|
|
496
|
+
# docker compose -p ${EDGE_PROJECT} -f ${options.dir}/docker-compose.yml down
|
|
497
|
+
${yaml$1(edgeCompose(options))}`;
|
|
498
|
+
}
|
|
499
|
+
function yaml$1(value) {
|
|
500
|
+
return (0, yaml.stringify)(value, {
|
|
501
|
+
lineWidth: 0,
|
|
502
|
+
aliasDuplicateObjects: false
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
//#endregion
|
|
506
|
+
//#region src/compose/edge.ts
|
|
507
|
+
/**
|
|
508
|
+
* The server's shared Traefik edge, on disk and in Docker: started when a
|
|
509
|
+
* stack needs it, and each stack's routes written to — and removed from —
|
|
510
|
+
* the directory it watches.
|
|
511
|
+
*
|
|
512
|
+
* The edge lives in the deploy user's gkm home (`$GKM_HOME/edge`, else
|
|
513
|
+
* `~/.gkm/edge`): the user who runs `gkm compose` owns it, so nothing needs
|
|
514
|
+
* root, and it sits beside the stage keys that same user already keeps.
|
|
515
|
+
*
|
|
516
|
+
* ~/.gkm/edge/
|
|
517
|
+
* docker-compose.yml the edge's compose project (gkm-edge)
|
|
518
|
+
* traefik.yml its static configuration
|
|
519
|
+
* dynamic/<project>.yml one per stack: its routers, services, middlewares
|
|
520
|
+
* certs/<project>.crt|.key a stack's own certificate, where it has one
|
|
521
|
+
*/
|
|
522
|
+
/** The edge's directory on this machine. */
|
|
523
|
+
function edgeDir(env = process.env) {
|
|
524
|
+
return (0, node_path.join)(require_run.gkmHome(env), "edge");
|
|
525
|
+
}
|
|
526
|
+
/** The edge's compose project, as docker is asked about it. */
|
|
527
|
+
function edgeRef(dir, options = {}) {
|
|
528
|
+
return {
|
|
529
|
+
project: EDGE_PROJECT,
|
|
530
|
+
file: (0, node_path.join)(dir, "docker-compose.yml"),
|
|
531
|
+
cwd: dir,
|
|
532
|
+
...options
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Something other than the proxy a stack is set to holds the edge's ports.
|
|
537
|
+
*
|
|
538
|
+
* With `proxy: 'traefik'`, the stack's own Caddy — still running from before
|
|
539
|
+
* the switch — or any other container; with `proxy: 'caddy'`, the shared
|
|
540
|
+
* edge, or another stack's Caddy. Refused before anything is started, so the
|
|
541
|
+
* stack is never left half on one proxy and half on the other.
|
|
542
|
+
*/
|
|
543
|
+
var ComposeProxyClash = class extends Error {
|
|
544
|
+
project;
|
|
545
|
+
proxy;
|
|
546
|
+
port;
|
|
547
|
+
holder;
|
|
548
|
+
constructor(project, proxy, port, holder) {
|
|
549
|
+
const by = holder.project ? `${holder.container} (compose project ${holder.project})` : holder.container;
|
|
550
|
+
const fix = proxy === "traefik" ? holder.project === project && holder.service === "caddy" ? `That is this stack's own Caddy, from before it was switched to proxy: 'traefik'. Move it once: stop it (docker compose -p ${project} stop caddy), then run gkm compose again — the shared edge takes 80/443 and the stack registers with it.` : `The shared edge needs ports 80 and 443. Stop ${holder.container}${holder.project ? ` (docker compose -p ${holder.project} down, or its own gkm compose --down)` : ""}, or move the edge with GKM_COMPOSE_HTTPS_PORT/GKM_COMPOSE_HTTP_PORT.` : holder.project === "gkm-edge" ? `That is the server's shared Traefik edge, which other stacks register with. Set deploy.compose.proxy to 'traefik' for this stage so the stack registers with it too, or stop the edge once nothing uses it (docker compose -p ${EDGE_PROJECT} down).` : `Stop ${holder.container}, or publish this stack's Caddy elsewhere with GKM_COMPOSE_HTTPS_PORT/GKM_COMPOSE_HTTP_PORT.`;
|
|
551
|
+
super(`${project} is set to proxy: '${proxy}', and port ${port} is held by ${by}. ${fix}`);
|
|
552
|
+
this.project = project;
|
|
553
|
+
this.proxy = proxy;
|
|
554
|
+
this.port = port;
|
|
555
|
+
this.holder = holder;
|
|
556
|
+
this.name = "ComposeProxyClash";
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
/**
|
|
560
|
+
* Refuse a stack whose edge ports are held by anything but its own proxy:
|
|
561
|
+
* the shared edge for `'traefik'`, the stack's own Caddy for `'caddy'`.
|
|
562
|
+
*/
|
|
563
|
+
async function assertEdgePorts(docker, options) {
|
|
564
|
+
const { project, proxy, ports } = options;
|
|
565
|
+
for (const port of [ports.https, ports.http]) for (const holder of await docker.publishers(port)) if (!(proxy === "traefik" ? holder.project === "gkm-edge" : holder.project === project && holder.service === "caddy")) throw new ComposeProxyClash(project, proxy, port, holder);
|
|
566
|
+
}
|
|
567
|
+
/** Write a file whole or not at all: a temporary file, then a rename. */
|
|
568
|
+
async function writeAtomic(path, content, mode = 420) {
|
|
569
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
570
|
+
await (0, node_fs_promises.writeFile)(temporary, content, { mode });
|
|
571
|
+
await (0, node_fs_promises.chmod)(temporary, mode);
|
|
572
|
+
await (0, node_fs_promises.rename)(temporary, path);
|
|
573
|
+
}
|
|
574
|
+
/** Write a file only when its content changed; whether it did. */
|
|
575
|
+
async function writeIfChanged(path, content) {
|
|
576
|
+
if (await (0, node_fs_promises.readFile)(path, "utf-8").catch(() => void 0) === content) return false;
|
|
577
|
+
await writeAtomic(path, content);
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* The edge, up: its network, its files, and its container — started when it
|
|
582
|
+
* is not running, and recreated when its configuration changed. Idempotent:
|
|
583
|
+
* on a server where it already runs as configured, `up` changes nothing.
|
|
584
|
+
*/
|
|
585
|
+
async function ensureEdge(docker, options) {
|
|
586
|
+
const { dir, ports } = options;
|
|
587
|
+
await (0, node_fs_promises.mkdir)((0, node_path.join)(dir, "dynamic"), { recursive: true });
|
|
588
|
+
await (0, node_fs_promises.mkdir)((0, node_path.join)(dir, "certs"), {
|
|
589
|
+
recursive: true,
|
|
590
|
+
mode: 448
|
|
591
|
+
});
|
|
592
|
+
const staticConfig = traefikStaticFile(ports);
|
|
593
|
+
await writeIfChanged((0, node_path.join)(dir, "traefik.yml"), staticConfig);
|
|
594
|
+
await writeIfChanged((0, node_path.join)(dir, "docker-compose.yml"), edgeComposeFile({
|
|
595
|
+
dir,
|
|
596
|
+
ports,
|
|
597
|
+
staticConfig,
|
|
598
|
+
logging: options.logging
|
|
599
|
+
}));
|
|
600
|
+
await docker.ensureNetwork(EDGE_NETWORK);
|
|
601
|
+
await docker.up(edgeRef(dir, {
|
|
602
|
+
...options.output ? { output: options.output } : {},
|
|
603
|
+
...options.signal ? { signal: options.signal } : {}
|
|
604
|
+
}), [EDGE_SERVICE]);
|
|
605
|
+
return [(0, node_path.join)(dir, "traefik.yml"), (0, node_path.join)(dir, "docker-compose.yml")];
|
|
606
|
+
}
|
|
607
|
+
/** A stack's file in the edge's directory. */
|
|
608
|
+
function routesFile(dir, project) {
|
|
609
|
+
return (0, node_path.join)(dir, "dynamic", `${project}.yml`);
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Register a stack with the edge: its certificate first, where it has one,
|
|
613
|
+
* so the routes that need it never load without it — then its routes, in one
|
|
614
|
+
* rename the edge picks up whole.
|
|
615
|
+
*/
|
|
616
|
+
async function writeEdgeRoutes(dir, project, routes, certificate) {
|
|
617
|
+
const files = [];
|
|
618
|
+
if (certificate) {
|
|
619
|
+
const certs = (0, node_path.join)(dir, "certs");
|
|
620
|
+
await (0, node_fs_promises.mkdir)(certs, {
|
|
621
|
+
recursive: true,
|
|
622
|
+
mode: 448
|
|
623
|
+
});
|
|
624
|
+
const crt = (0, node_path.join)(certs, `${project}.crt`);
|
|
625
|
+
const key = (0, node_path.join)(certs, `${project}.key`);
|
|
626
|
+
await (0, node_fs_promises.copyFile)(certificate.certFile, `${crt}.tmp`);
|
|
627
|
+
await (0, node_fs_promises.rename)(`${crt}.tmp`, crt);
|
|
628
|
+
await (0, node_fs_promises.copyFile)(certificate.keyFile, `${key}.tmp`);
|
|
629
|
+
await (0, node_fs_promises.chmod)(`${key}.tmp`, 384);
|
|
630
|
+
await (0, node_fs_promises.rename)(`${key}.tmp`, key);
|
|
631
|
+
files.push(crt, key);
|
|
632
|
+
} else await removeCertificate(dir, project);
|
|
633
|
+
await (0, node_fs_promises.mkdir)((0, node_path.join)(dir, "dynamic"), { recursive: true });
|
|
634
|
+
const file = routesFile(dir, project);
|
|
635
|
+
await writeAtomic(file, routes);
|
|
636
|
+
files.push(file);
|
|
637
|
+
return files;
|
|
638
|
+
}
|
|
639
|
+
/** What a stack's file holds for the moment between emptying and removal. */
|
|
640
|
+
const UNREGISTERED = "# Unregistered by gkm compose --down.\n";
|
|
641
|
+
async function removeCertificate(dir, project) {
|
|
642
|
+
for (const ext of ["crt", "key"]) await (0, node_fs_promises.rm)((0, node_path.join)(dir, "certs", `${project}.${ext}`), { force: true });
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Unregister a stack: its routes, then its certificate. Whether there was
|
|
646
|
+
* anything to remove.
|
|
647
|
+
*/
|
|
648
|
+
async function removeEdgeRoutes(dir, project) {
|
|
649
|
+
const file = routesFile(dir, project);
|
|
650
|
+
const existed = (0, node_fs.existsSync)(file);
|
|
651
|
+
if (existed) await writeAtomic(file, UNREGISTERED);
|
|
652
|
+
await (0, node_fs_promises.rm)(file, { force: true });
|
|
653
|
+
await removeCertificate(dir, project);
|
|
654
|
+
return existed;
|
|
655
|
+
}
|
|
254
656
|
//#endregion
|
|
255
657
|
//#region src/compose/images.ts
|
|
256
658
|
/**
|
|
@@ -496,9 +898,9 @@ function otlpHeaders(email, password) {
|
|
|
496
898
|
/** The stack's OpenObserve for one stage. */
|
|
497
899
|
function stackLogs(options) {
|
|
498
900
|
const { config, stage, local, custom } = options;
|
|
499
|
-
const conflicting = Object.keys(custom).filter(
|
|
901
|
+
const conflicting = Object.keys(custom).filter(require_seeds.isOtlpDestination).sort();
|
|
500
902
|
if (conflicting.length > 0) throw new LogsEndpointConflict(stage, conflicting);
|
|
501
|
-
if (!local && !options.domain) throw new
|
|
903
|
+
if (!local && !options.domain) throw new require_seeds.NoDomainForStage(stage);
|
|
502
904
|
const email = custom["ZO_ROOT_USER_EMAIL"] ?? (local ? "admin@gkm.localhost" : `admin@${options.domain}`);
|
|
503
905
|
const set = custom[LOGS_PASSWORD_KEY];
|
|
504
906
|
if (!set && !local) throw new LogsPasswordMissing(stage);
|
|
@@ -596,7 +998,7 @@ const REDIS_SERVICE = "redis";
|
|
|
596
998
|
/** The port it answers on, inside the network. */
|
|
597
999
|
const REDIS_PORT = 6379;
|
|
598
1000
|
/** Reconcile's pin, so `gkm dev` and the stack run one Redis. */
|
|
599
|
-
const REDIS_IMAGE =
|
|
1001
|
+
const REDIS_IMAGE = require_seeds.DEFAULT_IMAGES.redis;
|
|
600
1002
|
/**
|
|
601
1003
|
* How much it may hold before it evicts. A cache that grows until the box
|
|
602
1004
|
* runs out of memory takes the apps with it; 256 MB is a lot of sessions and
|
|
@@ -715,9 +1117,16 @@ function redisService(redis) {
|
|
|
715
1117
|
}
|
|
716
1118
|
//#endregion
|
|
717
1119
|
//#region src/compose/caddyfile.ts
|
|
1120
|
+
/** Where Caddy reads a stage's own certificate, inside its container. */
|
|
1121
|
+
const CADDY_TLS_DIR = "/etc/caddy/tls";
|
|
1122
|
+
/** An upstream on the stack's own network: its service name. */
|
|
1123
|
+
function address({ service, port }) {
|
|
1124
|
+
return `${service}:${port}`;
|
|
1125
|
+
}
|
|
718
1126
|
/** The Caddyfile for a stack's edge. */
|
|
719
|
-
function edgeCaddyfile(
|
|
720
|
-
const
|
|
1127
|
+
function edgeCaddyfile(routes, options) {
|
|
1128
|
+
const { tls } = options;
|
|
1129
|
+
const global = tls.kind === "internal" ? `{
|
|
721
1130
|
# No ACME and no public DNS: the CA is Caddy's own and the names resolve
|
|
722
1131
|
# to this machine.
|
|
723
1132
|
local_certs
|
|
@@ -725,27 +1134,30 @@ function edgeCaddyfile(sites, options) {
|
|
|
725
1134
|
}` : options.email ? `{
|
|
726
1135
|
email ${options.email}
|
|
727
1136
|
}` : "";
|
|
728
|
-
const
|
|
1137
|
+
const certificate = tls.kind === "internal" ? `
|
|
729
1138
|
tls internal
|
|
730
|
-
` : ""
|
|
1139
|
+
` : tls.kind === "files" ? `
|
|
1140
|
+
tls ${tls.certFile} ${tls.keyFile}
|
|
1141
|
+
` : "";
|
|
1142
|
+
const blocks = routes.map(({ host, upstreams, prefix, allow, streaming }) => `https://${host} {${certificate}${allow ? `
|
|
731
1143
|
# Only these addresses — the peer Caddy sees, never a header a client
|
|
732
1144
|
# could set. Everything else is refused before it reaches the upstream.
|
|
733
1145
|
@denied not remote_ip ${allow.join(" ")}
|
|
734
1146
|
respond @denied 403
|
|
735
|
-
` : ""}${
|
|
1147
|
+
` : ""}${prefix ? `
|
|
736
1148
|
# A bucket, served at a host of its own: the bucket is a prefix on MinIO,
|
|
737
1149
|
# which routes and signs on the Host header — so it is sent the upstream's.
|
|
738
|
-
reverse_proxy ${
|
|
739
|
-
rewrite ${
|
|
1150
|
+
reverse_proxy ${upstreams.map(address).join(" ")} {
|
|
1151
|
+
rewrite ${prefix}{uri}
|
|
740
1152
|
header_up Host {upstream_hostport}
|
|
741
1153
|
}` : `
|
|
742
1154
|
# Host and X-Forwarded-* are passed through as Caddy does by default, so
|
|
743
1155
|
# an app builds redirects, cookies and links on the address a caller used.
|
|
744
|
-
reverse_proxy ${
|
|
1156
|
+
reverse_proxy ${upstreams.map(address).join(" ")}${streaming ? ` {
|
|
745
1157
|
# Never buffer: a streamed response — server-sent events, a Next.js
|
|
746
1158
|
# RSC payload — reaches the client as each chunk is written.
|
|
747
1159
|
flush_interval -1
|
|
748
|
-
}`}
|
|
1160
|
+
}` : ""}`}
|
|
749
1161
|
}`);
|
|
750
1162
|
return `${HEADER}${global ? `${global}\n\n` : ""}${blocks.join("\n\n")}\n`;
|
|
751
1163
|
}
|
|
@@ -907,7 +1319,9 @@ function composeStack(input) {
|
|
|
907
1319
|
const custom = input.secrets?.custom ?? {};
|
|
908
1320
|
const https = input.ports?.https ?? 443;
|
|
909
1321
|
const http = input.ports?.http ?? 80;
|
|
910
|
-
const
|
|
1322
|
+
const proxy = require_workspace.proxyFor(workspace.deploy?.compose, stage, local);
|
|
1323
|
+
const certificate = local ? void 0 : require_workspace.certificateFor(workspace.deploy?.compose, stage, workspace.root);
|
|
1324
|
+
const plan = require_seeds.planFor(manifest, stage, (0, _geekmidas_manifest.provisionOrder)(manifest), {
|
|
911
1325
|
localStage: workspace.stages.local,
|
|
912
1326
|
events: require_SubscriberGenerator.DEFAULT_EVENTS.server,
|
|
913
1327
|
cache: STACK_CACHE,
|
|
@@ -917,7 +1331,7 @@ function composeStack(input) {
|
|
|
917
1331
|
const seed = local ? void 0 : input.secrets?.seed;
|
|
918
1332
|
if (!local && !seed) throw new StageSeedMissing(stage);
|
|
919
1333
|
const credential = {
|
|
920
|
-
master: local ? "geekmidas" :
|
|
1334
|
+
master: local ? "geekmidas" : require_seeds.localRolePassword(workspace.name, plan, "master", seed),
|
|
921
1335
|
...seed ? { seed } : {}
|
|
922
1336
|
};
|
|
923
1337
|
const apps = stackApps(workspace, manifest, plan, {
|
|
@@ -927,7 +1341,7 @@ function composeStack(input) {
|
|
|
927
1341
|
identity,
|
|
928
1342
|
images: input.images
|
|
929
1343
|
});
|
|
930
|
-
const workers = stackWorkers(
|
|
1344
|
+
const workers = stackWorkers(require_seeds.workerUnits(workspace, manifest, input.background ?? {}), {
|
|
931
1345
|
stage,
|
|
932
1346
|
identity,
|
|
933
1347
|
images: input.images
|
|
@@ -945,12 +1359,12 @@ function composeStack(input) {
|
|
|
945
1359
|
const reads = /* @__PURE__ */ new Map();
|
|
946
1360
|
for (const app of apps) {
|
|
947
1361
|
const site = manifest[app.id];
|
|
948
|
-
reads.set(app.name, site?.kind === "site" ? Object.values((0, _geekmidas_manifest.publicEnvFor)(site, manifest)) : [...
|
|
1362
|
+
reads.set(app.name, site?.kind === "site" ? Object.values((0, _geekmidas_manifest.publicEnvFor)(site, manifest)) : [...require_seeds.appEnvKeys(manifest, app.name, input.runnables) ?? []]);
|
|
949
1363
|
}
|
|
950
|
-
for (const worker of workers) reads.set(worker.name, [...
|
|
1364
|
+
for (const worker of workers) reads.set(worker.name, [...require_seeds.workerEnvKeys(manifest, worker.id, input.runnables) ?? []]);
|
|
951
1365
|
const domain = workspace.deploy?.domains?.[stage];
|
|
952
1366
|
const logsConfig = require_workspace.resolveLogs(workspace.deploy?.compose?.logs);
|
|
953
|
-
const
|
|
1367
|
+
const logsBase = logsConfig ? stackLogs({
|
|
954
1368
|
config: logsConfig,
|
|
955
1369
|
stage,
|
|
956
1370
|
local,
|
|
@@ -963,7 +1377,7 @@ function composeStack(input) {
|
|
|
963
1377
|
missing: [],
|
|
964
1378
|
minio: plan.resources.filter((r) => r.kind === "objects").map((r) => r.id),
|
|
965
1379
|
mailpit: plan.resources.filter((r) => r.kind === "email").map((r) => r.id)
|
|
966
|
-
} :
|
|
1380
|
+
} : require_seeds.assertExternalServices({
|
|
967
1381
|
stage,
|
|
968
1382
|
declarations: serviceDeclarations(plan, reads, owners),
|
|
969
1383
|
supplied: custom,
|
|
@@ -976,30 +1390,16 @@ function composeStack(input) {
|
|
|
976
1390
|
local,
|
|
977
1391
|
custom
|
|
978
1392
|
});
|
|
979
|
-
const infra = [...plan.containers.filter((c) => !NOT_RUN[c]).filter((c) => c !== "minio" || services.minio.length > 0).filter((c) => c !== "mailpit" || services.mailpit.length > 0).filter((c) => c !== "redis" || redis !== void 0), ...
|
|
1393
|
+
const infra = [...plan.containers.filter((c) => !NOT_RUN[c]).filter((c) => c !== "minio" || services.minio.length > 0).filter((c) => c !== "mailpit" || services.mailpit.length > 0).filter((c) => c !== "redis" || redis !== void 0), ...logsBase ? [LOGS_SERVICE] : []].sort();
|
|
980
1394
|
const storage = infra.includes("minio") ? (() => {
|
|
981
1395
|
const buckets = services.minio.map((id) => byId.get(id)?.name).filter((name) => Boolean(name)).sort();
|
|
982
1396
|
return {
|
|
983
1397
|
user: custom.AWS_ACCESS_KEY_ID ?? (local ? "geekmidas" : `${workspace.name}-minio`),
|
|
984
|
-
password: custom.AWS_SECRET_ACCESS_KEY ?? (local ? "geekmidas" :
|
|
1398
|
+
password: custom.AWS_SECRET_ACCESS_KEY ?? (local ? "geekmidas" : require_seeds.localRolePassword(workspace.name, plan, "minio", seed)),
|
|
985
1399
|
buckets,
|
|
986
|
-
policies:
|
|
1400
|
+
policies: require_seeds.bucketPolicies(plan).filter((p) => buckets.includes(p.bucket))
|
|
987
1401
|
};
|
|
988
1402
|
})() : void 0;
|
|
989
|
-
const derivation = {
|
|
990
|
-
project: workspace.name,
|
|
991
|
-
master: credential.master,
|
|
992
|
-
...credential.seed ? { seed: credential.seed } : {},
|
|
993
|
-
...!local && domain ? { mailFrom: `noreply@${domain}` } : {}
|
|
994
|
-
};
|
|
995
|
-
const outside = require_stale.networkEnv(plan, {
|
|
996
|
-
...derivation,
|
|
997
|
-
addresses: Object.fromEntries(apps.map((app) => [app.id, app.url]))
|
|
998
|
-
});
|
|
999
|
-
const inside = require_stale.networkEnv(plan, {
|
|
1000
|
-
...derivation,
|
|
1001
|
-
addresses: Object.fromEntries(apps.map((app) => [app.id, `http://${app.name}:${app.port}`]))
|
|
1002
|
-
});
|
|
1003
1403
|
const fileServers = /* @__PURE__ */ new Map();
|
|
1004
1404
|
if (storage) for (const resource of plan.resources) {
|
|
1005
1405
|
if (resource.kind !== "file-server" || !resource.of) continue;
|
|
@@ -1008,20 +1408,52 @@ function composeStack(input) {
|
|
|
1008
1408
|
const bucket = byId.get(resource.of);
|
|
1009
1409
|
if (!bucket) continue;
|
|
1010
1410
|
let host;
|
|
1011
|
-
if (local) host =
|
|
1411
|
+
if (local) host = require_seeds.hostFor(resource, workspace.name);
|
|
1012
1412
|
else {
|
|
1013
|
-
if (!domain) throw new
|
|
1413
|
+
if (!domain) throw new require_seeds.NoDomainForStage(stage);
|
|
1014
1414
|
host = `${resource.subdomain ?? require_derive.appKey(resource.id)}.${domain}`;
|
|
1015
1415
|
}
|
|
1016
1416
|
fileServers.set(resource.id, {
|
|
1017
1417
|
url: `https://${host}${local && https !== 443 ? `:${https}` : ""}`,
|
|
1018
|
-
|
|
1418
|
+
route: {
|
|
1419
|
+
name: `files-${require_derive.appKey(resource.id)}`,
|
|
1019
1420
|
host,
|
|
1020
|
-
|
|
1021
|
-
|
|
1421
|
+
upstreams: [{
|
|
1422
|
+
service: "minio",
|
|
1423
|
+
port: 9e3
|
|
1424
|
+
}],
|
|
1425
|
+
streaming: false,
|
|
1426
|
+
prefix: `/${bucket.name}`
|
|
1022
1427
|
}
|
|
1023
1428
|
});
|
|
1024
1429
|
}
|
|
1430
|
+
const edgeServices = new Set(proxy === "traefik" ? [
|
|
1431
|
+
...apps.map((app) => app.name),
|
|
1432
|
+
...fileServers.size > 0 ? ["minio"] : [],
|
|
1433
|
+
...logsBase?.host && logsBase.public ? [LOGS_SERVICE] : []
|
|
1434
|
+
] : []);
|
|
1435
|
+
const hostOf = (service) => edgeServices.has(service) ? edgeAlias(project, service) : service;
|
|
1436
|
+
const logs = logsBase && {
|
|
1437
|
+
...logsBase,
|
|
1438
|
+
appEnv: {
|
|
1439
|
+
...logsBase.appEnv,
|
|
1440
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: logsBase.appEnv.OTEL_EXPORTER_OTLP_ENDPOINT.replace(`//openobserve:`, `//${hostOf("openobserve")}:`)
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
const derivation = {
|
|
1444
|
+
project: workspace.name,
|
|
1445
|
+
master: credential.master,
|
|
1446
|
+
...credential.seed ? { seed: credential.seed } : {},
|
|
1447
|
+
...!local && domain ? { mailFrom: `noreply@${domain}` } : {}
|
|
1448
|
+
};
|
|
1449
|
+
const outside = require_seeds.networkEnv(plan, {
|
|
1450
|
+
...derivation,
|
|
1451
|
+
addresses: Object.fromEntries(apps.map((app) => [app.id, app.url]))
|
|
1452
|
+
}, hostOf);
|
|
1453
|
+
const inside = require_seeds.networkEnv(plan, {
|
|
1454
|
+
...derivation,
|
|
1455
|
+
addresses: Object.fromEntries(apps.map((app) => [app.id, `http://${hostOf(app.name)}:${app.port}`]))
|
|
1456
|
+
}, hostOf);
|
|
1025
1457
|
const surfaceUrls = new Map(plan.resources.filter((r) => r.kind === "rest-api" || r.kind === "site").map((r) => [r.envKey, r.id]));
|
|
1026
1458
|
const appById = new Map(apps.map((app) => [app.id, app]));
|
|
1027
1459
|
/**
|
|
@@ -1034,7 +1466,7 @@ function composeStack(input) {
|
|
|
1034
1466
|
if (owner.kind === "file-server") return fileServers.get(owner.id)?.url;
|
|
1035
1467
|
return outside[key];
|
|
1036
1468
|
}
|
|
1037
|
-
if (owner &&
|
|
1469
|
+
if (owner && require_seeds.suppliedOnly(owner.kind, owner.id, key, local)) {
|
|
1038
1470
|
const value = custom[key];
|
|
1039
1471
|
if (value === void 0) throw new StageSecretMissing(app.name, key, stage);
|
|
1040
1472
|
return value;
|
|
@@ -1074,17 +1506,17 @@ function composeStack(input) {
|
|
|
1074
1506
|
continue;
|
|
1075
1507
|
}
|
|
1076
1508
|
if (input.buildOnly) continue;
|
|
1077
|
-
const keys =
|
|
1509
|
+
const keys = require_seeds.appEnvKeys(manifest, app.name, input.runnables) ?? /* @__PURE__ */ new Set();
|
|
1078
1510
|
const values = valuesFor(app, keys);
|
|
1079
1511
|
const trusted = (0, _geekmidas_manifest.provideKey)(app.id, "trustedOrigins");
|
|
1080
1512
|
if (keys.has(trusted)) {
|
|
1081
|
-
const internal = (byId.get(app.id)?.callers ?? []).map((caller) => appById.get(caller)).filter((caller) => caller?.kind === "rest-api").map((caller) => `http://${caller.name}:${caller.port}`);
|
|
1513
|
+
const internal = (byId.get(app.id)?.callers ?? []).map((caller) => appById.get(caller)).filter((caller) => caller?.kind === "rest-api").map((caller) => `http://${hostOf(caller.name)}:${caller.port}`);
|
|
1082
1514
|
const origins = [...(values[trusted] ?? "").split(",").filter(Boolean), ...internal];
|
|
1083
1515
|
values[trusted] = [...new Set(origins)].join(",");
|
|
1084
1516
|
}
|
|
1085
1517
|
app.env = {
|
|
1086
1518
|
...values,
|
|
1087
|
-
...
|
|
1519
|
+
...require_seeds.otelEnv({
|
|
1088
1520
|
...custom,
|
|
1089
1521
|
...logs?.appEnv
|
|
1090
1522
|
}, app.name),
|
|
@@ -1095,14 +1527,14 @@ function composeStack(input) {
|
|
|
1095
1527
|
for (const [key, value] of Object.entries(app.env)) if (/[\r\n]/.test(value)) throw new EnvValueMultiline(app.name, key);
|
|
1096
1528
|
}
|
|
1097
1529
|
for (const worker of input.buildOnly ? [] : workers) {
|
|
1098
|
-
const keys =
|
|
1530
|
+
const keys = require_seeds.workerEnvKeys(manifest, worker.id, input.runnables) ?? /* @__PURE__ */ new Set();
|
|
1099
1531
|
worker.env = {
|
|
1100
1532
|
...valuesFor({
|
|
1101
1533
|
name: worker.name,
|
|
1102
1534
|
id: worker.id,
|
|
1103
1535
|
kind: "rest-api"
|
|
1104
1536
|
}, keys),
|
|
1105
|
-
...
|
|
1537
|
+
...require_seeds.otelEnv({
|
|
1106
1538
|
...custom,
|
|
1107
1539
|
...logs?.appEnv
|
|
1108
1540
|
}, worker.name),
|
|
@@ -1112,6 +1544,14 @@ function composeStack(input) {
|
|
|
1112
1544
|
};
|
|
1113
1545
|
for (const [key, value] of Object.entries(worker.env)) if (/[\r\n]/.test(value)) throw new EnvValueMultiline(worker.name, key);
|
|
1114
1546
|
}
|
|
1547
|
+
const deployedTls = certificate ? {
|
|
1548
|
+
kind: "files",
|
|
1549
|
+
...proxy === "caddy" ? {
|
|
1550
|
+
certFile: `${CADDY_TLS_DIR}/cert.pem`,
|
|
1551
|
+
keyFile: `${CADDY_TLS_DIR}/key.pem`
|
|
1552
|
+
} : edgeCertificatePaths(project)
|
|
1553
|
+
} : { kind: "acme" };
|
|
1554
|
+
const tls = local ? { kind: "internal" } : deployedTls;
|
|
1115
1555
|
const layout = input.layout ?? defaultLayout(workspace.root);
|
|
1116
1556
|
const compose = stackFile({
|
|
1117
1557
|
project,
|
|
@@ -1128,24 +1568,38 @@ function composeStack(input) {
|
|
|
1128
1568
|
composeDir: (0, node_path.join)(workspace.root, stackDir(stage)),
|
|
1129
1569
|
buildRoot: layout.buildRoot,
|
|
1130
1570
|
workspaceRoot: workspace.root,
|
|
1131
|
-
envFiles: !input.buildOnly
|
|
1571
|
+
envFiles: !input.buildOnly,
|
|
1572
|
+
proxy,
|
|
1573
|
+
edge: new Map([...edgeServices].map((service) => [service, hostOf(service)])),
|
|
1574
|
+
tls: certificate !== void 0
|
|
1132
1575
|
});
|
|
1133
|
-
const
|
|
1576
|
+
const routes = [
|
|
1134
1577
|
...apps.map((app) => ({
|
|
1578
|
+
name: app.name,
|
|
1135
1579
|
host: app.host,
|
|
1136
|
-
|
|
1580
|
+
upstreams: [{
|
|
1581
|
+
service: app.name,
|
|
1582
|
+
port: app.port
|
|
1583
|
+
}],
|
|
1584
|
+
streaming: true,
|
|
1585
|
+
health: app.kind === "site" ? "/" : "/health"
|
|
1137
1586
|
})),
|
|
1138
|
-
...[...fileServers.values()].map(({
|
|
1587
|
+
...[...fileServers.values()].map(({ route }) => route),
|
|
1139
1588
|
...logs?.host && logs.public ? [{
|
|
1589
|
+
name: LOGS_SERVICE,
|
|
1140
1590
|
host: logs.host,
|
|
1141
|
-
|
|
1591
|
+
upstreams: [{
|
|
1592
|
+
service: LOGS_SERVICE,
|
|
1593
|
+
port: require_workspace.LOGS_PORT
|
|
1594
|
+
}],
|
|
1595
|
+
streaming: true,
|
|
1142
1596
|
allow: logs.public.allow
|
|
1143
1597
|
}] : []
|
|
1144
|
-
]
|
|
1598
|
+
];
|
|
1145
1599
|
const dockerfiles = {};
|
|
1146
1600
|
for (const app of apps) {
|
|
1147
1601
|
if (!app.build) continue;
|
|
1148
|
-
dockerfiles[app.build.dockerfile] =
|
|
1602
|
+
dockerfiles[app.build.dockerfile] = require_seeds.appDockerfile(app.name, workspace.apps[app.name], {
|
|
1149
1603
|
layout,
|
|
1150
1604
|
workspaceRoot: workspace.root,
|
|
1151
1605
|
apps: workspace.apps,
|
|
@@ -1155,7 +1609,7 @@ function composeStack(input) {
|
|
|
1155
1609
|
}
|
|
1156
1610
|
for (const worker of workers) {
|
|
1157
1611
|
if (!worker.build) continue;
|
|
1158
|
-
dockerfiles[worker.build.dockerfile] =
|
|
1612
|
+
dockerfiles[worker.build.dockerfile] = require_seeds.workerDockerfile({
|
|
1159
1613
|
id: worker.id,
|
|
1160
1614
|
name: worker.name,
|
|
1161
1615
|
app: worker.host
|
|
@@ -1173,12 +1627,18 @@ function composeStack(input) {
|
|
|
1173
1627
|
workers,
|
|
1174
1628
|
infra,
|
|
1175
1629
|
compose,
|
|
1176
|
-
|
|
1630
|
+
proxy,
|
|
1631
|
+
routes,
|
|
1632
|
+
...proxy === "caddy" ? { caddyfile: edgeCaddyfile(routes, { tls }) } : { traefik: traefikDynamicFile(routes, {
|
|
1633
|
+
project,
|
|
1634
|
+
tls: deployedTls
|
|
1635
|
+
}) },
|
|
1636
|
+
...certificate ? { tls: certificate } : {},
|
|
1177
1637
|
dockerfiles,
|
|
1178
1638
|
plan,
|
|
1179
1639
|
credential,
|
|
1180
1640
|
...storage ? { storage } : {},
|
|
1181
|
-
devServices: local ? [] :
|
|
1641
|
+
devServices: local ? [] : require_seeds.devServicesUsed(services),
|
|
1182
1642
|
...logs ? { logs } : {},
|
|
1183
1643
|
...redis ? { redis } : {}
|
|
1184
1644
|
};
|
|
@@ -1239,7 +1699,7 @@ function stackApps(workspace, manifest, plan, options) {
|
|
|
1239
1699
|
if (!app || !resource || !app.port) continue;
|
|
1240
1700
|
const host = hostOf(name, app, workspace, resource, options);
|
|
1241
1701
|
const tag = declaration.kind === "site" ? siteTag(options.images.tag, options.stage) : options.images.tag;
|
|
1242
|
-
const ref =
|
|
1702
|
+
const ref = require_seeds.validateImageRef(require_seeds.imageRef(options.identity, name, options.images.registry, tag));
|
|
1243
1703
|
apps.push({
|
|
1244
1704
|
app: name,
|
|
1245
1705
|
name,
|
|
@@ -1263,8 +1723,8 @@ function stackWorkers(units, options) {
|
|
|
1263
1723
|
name: unit.name,
|
|
1264
1724
|
id: unit.id,
|
|
1265
1725
|
host: unit.app,
|
|
1266
|
-
port:
|
|
1267
|
-
ref:
|
|
1726
|
+
port: require_seeds.WORKER_PORT,
|
|
1727
|
+
ref: require_seeds.validateImageRef(require_seeds.imageRef(options.identity, unit.name, options.images.registry, options.images.tag)),
|
|
1268
1728
|
tag: options.images.tag,
|
|
1269
1729
|
...options.images.mode === "build" ? { build: { dockerfile: `${stackDir(options.stage)}/Dockerfile.${unit.name}` } } : {}
|
|
1270
1730
|
}));
|
|
@@ -1275,25 +1735,27 @@ function stackWorkers(units, options) {
|
|
|
1275
1735
|
* same rule a deploy follows — for any other.
|
|
1276
1736
|
*/
|
|
1277
1737
|
function hostOf(name, app, workspace, resource, options) {
|
|
1278
|
-
if (options.local) return
|
|
1279
|
-
return
|
|
1738
|
+
if (options.local) return require_seeds.hostFor(resource, workspace.name);
|
|
1739
|
+
return require_seeds.resolveHost(name, app, options.stage, workspace.deploy?.domains, require_seeds.isMainFrontendApp(name, app, workspace.apps));
|
|
1280
1740
|
}
|
|
1281
1741
|
/** The compose document: the infrastructure, the apps, and the edge. */
|
|
1282
1742
|
function stackFile(options) {
|
|
1283
1743
|
const { project, plan, apps } = options;
|
|
1284
1744
|
const infra = options.infra.filter((name) => name !== LOGS_SERVICE);
|
|
1285
|
-
const derived =
|
|
1745
|
+
const derived = require_seeds.composeFor({
|
|
1286
1746
|
...plan,
|
|
1287
1747
|
containers: [...infra]
|
|
1288
1748
|
}, {
|
|
1289
1749
|
project,
|
|
1290
|
-
ports: Object.fromEntries(
|
|
1750
|
+
ports: Object.fromEntries(require_seeds.portKeys(infra, plan.fakes).map((key) => [key, 0]))
|
|
1291
1751
|
});
|
|
1292
1752
|
const services = {};
|
|
1293
1753
|
const volumes = {
|
|
1294
1754
|
...derived.volumes,
|
|
1295
|
-
"caddy
|
|
1296
|
-
|
|
1755
|
+
...options.proxy === "caddy" ? {
|
|
1756
|
+
"caddy-data": {},
|
|
1757
|
+
"caddy-config": {}
|
|
1758
|
+
} : {},
|
|
1297
1759
|
...options.logs ? { "openobserve-data": {} } : {}
|
|
1298
1760
|
};
|
|
1299
1761
|
for (const name of infra) {
|
|
@@ -1325,7 +1787,7 @@ function stackFile(options) {
|
|
|
1325
1787
|
for (const app of apps) services[app.name] = {
|
|
1326
1788
|
image: app.ref,
|
|
1327
1789
|
...app.build ? { build: {
|
|
1328
|
-
...
|
|
1790
|
+
...require_seeds.composeBuildPaths({
|
|
1329
1791
|
composeDir: options.composeDir,
|
|
1330
1792
|
buildRoot: options.buildRoot,
|
|
1331
1793
|
workspaceRoot: options.workspaceRoot,
|
|
@@ -1345,7 +1807,7 @@ function stackFile(options) {
|
|
|
1345
1807
|
};
|
|
1346
1808
|
for (const worker of options.workers) services[worker.name] = {
|
|
1347
1809
|
image: worker.ref,
|
|
1348
|
-
...worker.build ? { build:
|
|
1810
|
+
...worker.build ? { build: require_seeds.composeBuildPaths({
|
|
1349
1811
|
composeDir: options.composeDir,
|
|
1350
1812
|
buildRoot: options.buildRoot,
|
|
1351
1813
|
workspaceRoot: options.workspaceRoot,
|
|
@@ -1359,14 +1821,15 @@ function stackFile(options) {
|
|
|
1359
1821
|
...infra.length ? { depends_on: Object.fromEntries(infra.map((name) => [name, HEALTHY])) } : {},
|
|
1360
1822
|
healthcheck: probe(worker.port, "/health")
|
|
1361
1823
|
};
|
|
1362
|
-
services.caddy = {
|
|
1363
|
-
image:
|
|
1824
|
+
if (options.proxy === "caddy") services.caddy = {
|
|
1825
|
+
image: require_seeds.DEFAULT_IMAGES.caddy,
|
|
1364
1826
|
restart: "unless-stopped",
|
|
1365
1827
|
ports: [`${options.https}:443`, `${options.http}:80`],
|
|
1366
1828
|
volumes: [
|
|
1367
1829
|
"./Caddyfile:/etc/caddy/Caddyfile:ro",
|
|
1368
1830
|
"caddy-data:/data",
|
|
1369
|
-
"caddy-config:/config"
|
|
1831
|
+
"caddy-config:/config",
|
|
1832
|
+
...options.tls ? [`./tls:${CADDY_TLS_DIR}:ro`] : []
|
|
1370
1833
|
],
|
|
1371
1834
|
depends_on: Object.fromEntries(apps.map((app) => [app.name, HEALTHY])),
|
|
1372
1835
|
healthcheck: {
|
|
@@ -1380,6 +1843,14 @@ function stackFile(options) {
|
|
|
1380
1843
|
retries: 5
|
|
1381
1844
|
}
|
|
1382
1845
|
};
|
|
1846
|
+
for (const [service, alias] of options.edge) {
|
|
1847
|
+
const definition = services[service];
|
|
1848
|
+
if (!definition) continue;
|
|
1849
|
+
definition.networks = {
|
|
1850
|
+
default: { aliases: [alias] },
|
|
1851
|
+
edge: { aliases: [alias] }
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1383
1854
|
for (const service of Object.values(services)) {
|
|
1384
1855
|
if (!options.envFiles) delete service.env_file;
|
|
1385
1856
|
service.logging = {
|
|
@@ -1390,7 +1861,11 @@ function stackFile(options) {
|
|
|
1390
1861
|
return {
|
|
1391
1862
|
name: project,
|
|
1392
1863
|
services,
|
|
1393
|
-
volumes
|
|
1864
|
+
volumes,
|
|
1865
|
+
...options.edge.size > 0 ? { networks: { edge: {
|
|
1866
|
+
name: EDGE_NETWORK,
|
|
1867
|
+
external: true
|
|
1868
|
+
} } } : {}
|
|
1394
1869
|
};
|
|
1395
1870
|
}
|
|
1396
1871
|
/**
|
|
@@ -1403,7 +1878,7 @@ function defaultLayout(root) {
|
|
|
1403
1878
|
gkmRoot: ".",
|
|
1404
1879
|
tools: {
|
|
1405
1880
|
packageManager: "pnpm",
|
|
1406
|
-
turboVersion:
|
|
1881
|
+
turboVersion: require_seeds.TURBO_VERSION,
|
|
1407
1882
|
monorepo: true
|
|
1408
1883
|
},
|
|
1409
1884
|
gkmPaths: ["gkm.config.*"]
|
|
@@ -1437,12 +1912,12 @@ var HealthRequestTimedOut = class extends Error {
|
|
|
1437
1912
|
}
|
|
1438
1913
|
};
|
|
1439
1914
|
/** The real probe: one HTTPS GET, the certificate verified. */
|
|
1440
|
-
const httpsProbe = ({ url, ca, connectTo, timeoutMs, signal }) => {
|
|
1915
|
+
const httpsProbe = ({ url, ca, connectTo, connectPort, timeoutMs, signal }) => {
|
|
1441
1916
|
const target = new URL(url);
|
|
1442
1917
|
return new Promise((resolve, reject) => {
|
|
1443
1918
|
const req = (0, node_https.request)({
|
|
1444
1919
|
host: connectTo ?? target.hostname,
|
|
1445
|
-
port: target.port || 443,
|
|
1920
|
+
port: connectPort ?? (target.port || 443),
|
|
1446
1921
|
servername: target.hostname,
|
|
1447
1922
|
path: `${target.pathname}${target.search}`,
|
|
1448
1923
|
method: "GET",
|
|
@@ -1468,10 +1943,13 @@ function isHealthy(status) {
|
|
|
1468
1943
|
var ComposeAppsUnhealthy = class extends Error {
|
|
1469
1944
|
project;
|
|
1470
1945
|
apps;
|
|
1471
|
-
|
|
1472
|
-
|
|
1946
|
+
proxy;
|
|
1947
|
+
constructor(project, apps, proxy = "caddy") {
|
|
1948
|
+
const names = apps.map((a) => a.app).join(" ");
|
|
1949
|
+
super(`${project} started, but ${apps.length === 1 ? "this app does" : "these apps do"} not answer through ${proxy === "caddy" ? "Caddy" : "the shared Traefik edge"}:\n` + apps.map(({ url, last }) => ` - ${url}: ${last}`).join("\n") + (proxy === "caddy" ? `\nSee why with \`docker compose -p ${project} logs caddy ${names}\`.` : `\nSee why with \`docker compose -p gkm-edge logs traefik\` and \`docker compose -p ${project} logs ${names}\`.`));
|
|
1473
1950
|
this.project = project;
|
|
1474
1951
|
this.apps = apps;
|
|
1952
|
+
this.proxy = proxy;
|
|
1475
1953
|
this.name = "ComposeAppsUnhealthy";
|
|
1476
1954
|
}
|
|
1477
1955
|
};
|
|
@@ -1487,8 +1965,9 @@ var ComposeAppsUnhealthy = class extends Error {
|
|
|
1487
1965
|
* is asked of the registry first; one missing image stops the run naming
|
|
1488
1966
|
* them all, and nothing is written, pulled or started.
|
|
1489
1967
|
* - The databases exist before the apps do (`provision`). Postgres comes up
|
|
1490
|
-
* alone, its databases, roles and grants are created
|
|
1491
|
-
* applied from this machine, and only then do the apps
|
|
1968
|
+
* alone, its databases, roles and grants are created, its migrations
|
|
1969
|
+
* applied and its seeds run from this machine, and only then do the apps
|
|
1970
|
+
* start — so an app
|
|
1492
1971
|
* never boots against a schema that is not there yet.
|
|
1493
1972
|
* - What a stage runs is recorded (`release`). Each app's image, the tag it
|
|
1494
1973
|
* was released under and the digest it resolved to are written to the
|
|
@@ -1583,9 +2062,10 @@ function edgePorts(env = process.env) {
|
|
|
1583
2062
|
const defaultDeps = {
|
|
1584
2063
|
docker: dockerCompose,
|
|
1585
2064
|
revision: gitRevision,
|
|
1586
|
-
sql:
|
|
1587
|
-
buckets:
|
|
2065
|
+
sql: require_seeds.pgClient,
|
|
2066
|
+
buckets: require_seeds.bucketClient,
|
|
1588
2067
|
migrate: require_databases.migrateDatabases,
|
|
2068
|
+
seed: require_databases.seedDatabases,
|
|
1589
2069
|
probe: httpsProbe,
|
|
1590
2070
|
healthAttempts: 90,
|
|
1591
2071
|
healthIntervalMs: 2e3,
|
|
@@ -1604,31 +2084,31 @@ async function validateCompose(ctx, deps, options = {}) {
|
|
|
1604
2084
|
if (push && mode !== "build") throw new ComposePushNeedsBuild();
|
|
1605
2085
|
if (options.pin && mode !== "pull") throw new ComposePinNeedsPull();
|
|
1606
2086
|
const registry = workspace.deploy?.registry;
|
|
1607
|
-
if (!registry && (push || mode === "pull")) throw new RegistryRequired(push ? "push" : "pull",
|
|
2087
|
+
if (!registry && (push || mode === "pull")) throw new RegistryRequired(push ? "push" : "pull", require_seeds.imageRef(identity, ctx.apps[0] ?? workspace.name, void 0, tag));
|
|
1608
2088
|
const runnables = {};
|
|
1609
2089
|
const background = {};
|
|
1610
2090
|
const manifest = await require_discover.discover({
|
|
1611
|
-
patterns:
|
|
2091
|
+
patterns: require_seeds.constructGlobs(workspace),
|
|
1612
2092
|
cwd: root,
|
|
1613
2093
|
runnables,
|
|
1614
2094
|
background
|
|
1615
2095
|
});
|
|
1616
2096
|
const { secrets, generated } = await stageSecrets(ctx, manifest);
|
|
1617
2097
|
if (!push) {
|
|
1618
|
-
|
|
2098
|
+
require_seeds.assertNoStaleSecrets({
|
|
1619
2099
|
manifest,
|
|
1620
2100
|
stage,
|
|
1621
2101
|
supplied: secrets?.custom ?? {}
|
|
1622
2102
|
});
|
|
1623
2103
|
await require_credentialSchemas.assertStageCredentials({
|
|
1624
2104
|
root,
|
|
1625
|
-
patterns:
|
|
2105
|
+
patterns: require_seeds.constructGlobs(workspace),
|
|
1626
2106
|
manifest,
|
|
1627
2107
|
stage,
|
|
1628
2108
|
supplied: secrets?.custom ?? {}
|
|
1629
2109
|
});
|
|
1630
2110
|
}
|
|
1631
|
-
const layout = mode === "build" ?
|
|
2111
|
+
const layout = mode === "build" ? require_seeds.imageLayout(workspace) : void 0;
|
|
1632
2112
|
const composed = composeStack({
|
|
1633
2113
|
workspace,
|
|
1634
2114
|
manifest,
|
|
@@ -1647,10 +2127,13 @@ async function validateCompose(ctx, deps, options = {}) {
|
|
|
1647
2127
|
...layout ? { layout } : {},
|
|
1648
2128
|
...push ? { buildOnly: true } : {}
|
|
1649
2129
|
});
|
|
1650
|
-
|
|
2130
|
+
require_seeds.reportDevServices(ctx, composed.devServices);
|
|
1651
2131
|
if (mode === "build") assertRedisClient(workspace, composed);
|
|
1652
2132
|
const stack = options.pin ? withPinnedImages(composed, options.pin.file, options.pin.digests) : composed;
|
|
1653
2133
|
if (mode === "pull" && !ctx.dryRun) await assertImagesExist(deps.docker, tag, [...stack.apps, ...stack.workers]);
|
|
2134
|
+
if (stack.tls && !push) {
|
|
2135
|
+
for (const file of [stack.tls.certFile, stack.tls.keyFile]) if (!(0, node_fs.existsSync)(file)) throw new require_workspace.ComposeTlsFileMissing(stage, file);
|
|
2136
|
+
}
|
|
1654
2137
|
if (stack.redis && !stack.local) ctx.secrets.mask(stack.redis.password);
|
|
1655
2138
|
if (stack.logs && (!stack.local || stack.logs.passwordFromSecrets)) {
|
|
1656
2139
|
ctx.secrets.mask(stack.logs.password);
|
|
@@ -1724,7 +2207,7 @@ async function stageSecrets(ctx, manifest) {
|
|
|
1724
2207
|
secrets: stored,
|
|
1725
2208
|
generated: []
|
|
1726
2209
|
};
|
|
1727
|
-
const withSeed =
|
|
2210
|
+
const withSeed = require_seeds.withGeneratedSecrets(stored ?? require_seeds.initStageSecrets(ctx.stage), manifest);
|
|
1728
2211
|
const withLogs = require_workspace.resolveLogs(ctx.workspace.deploy?.compose?.logs) ? withLogsPassword(withSeed.secrets) : {
|
|
1729
2212
|
secrets: withSeed.secrets,
|
|
1730
2213
|
generated: []
|
|
@@ -1778,6 +2261,13 @@ async function planCompose(ctx, run) {
|
|
|
1778
2261
|
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.`);
|
|
1779
2262
|
run.files = await writeStack(ctx.cwd, run.dir, run.stack);
|
|
1780
2263
|
printPlan(ctx, run);
|
|
2264
|
+
if (!run.push && run.stack.infra.includes("postgres")) require_seeds.reportPlannedSeeds((await require_databases.plannedSeeds({
|
|
2265
|
+
root: ctx.workspace.root,
|
|
2266
|
+
manifest: ctx.manifest
|
|
2267
|
+
})).map(({ target, seeds }) => ({
|
|
2268
|
+
folder: target.seeds,
|
|
2269
|
+
seeds
|
|
2270
|
+
})), (line) => ctx.logger.info(line));
|
|
1781
2271
|
const planned = (change) => {
|
|
1782
2272
|
run.changes.push(change);
|
|
1783
2273
|
ctx.emit({
|
|
@@ -1819,7 +2309,7 @@ async function planCompose(ctx, run) {
|
|
|
1819
2309
|
});
|
|
1820
2310
|
}
|
|
1821
2311
|
if (!run.push) planned({
|
|
1822
|
-
key: "service:caddy"
|
|
2312
|
+
key: run.stack.proxy === "caddy" ? "service:caddy" : `service:${EDGE_PROJECT}/${EDGE_SERVICE}`,
|
|
1823
2313
|
resourceType: "service",
|
|
1824
2314
|
action: "ensure"
|
|
1825
2315
|
});
|
|
@@ -1836,8 +2326,35 @@ async function provisionCompose(ctx, run, deps) {
|
|
|
1836
2326
|
await ctx.secrets.write(run.secrets);
|
|
1837
2327
|
ctx.logger.info(`🔑 Generated for "${ctx.stage}" (${ctx.secrets.store}): ${run.generated.join(", ")}`);
|
|
1838
2328
|
}
|
|
2329
|
+
const ports = edgePorts(deps.env);
|
|
2330
|
+
await assertEdgePorts(deps.docker, {
|
|
2331
|
+
project: stack.project,
|
|
2332
|
+
proxy: stack.proxy,
|
|
2333
|
+
ports
|
|
2334
|
+
});
|
|
1839
2335
|
run.files = await writeStack(ctx.cwd, run.dir, stack);
|
|
1840
2336
|
printPlan(ctx, run);
|
|
2337
|
+
if (stack.proxy === "traefik") {
|
|
2338
|
+
const dir = edgeDir(deps.env);
|
|
2339
|
+
ctx.logger.info(`\n🌐 Starting the shared edge (${EDGE_PROJECT}) from ${dir}…`);
|
|
2340
|
+
run.files.push(...await ensureEdge(deps.docker, {
|
|
2341
|
+
dir,
|
|
2342
|
+
ports,
|
|
2343
|
+
logging: {
|
|
2344
|
+
driver: LOG_ROTATION.driver,
|
|
2345
|
+
options: { ...LOG_ROTATION.options }
|
|
2346
|
+
},
|
|
2347
|
+
...ref.output ? { output: ref.output } : {},
|
|
2348
|
+
...ref.signal ? { signal: ref.signal } : {}
|
|
2349
|
+
}));
|
|
2350
|
+
applied(ctx, run, {
|
|
2351
|
+
key: `service:${EDGE_PROJECT}/${EDGE_SERVICE}`,
|
|
2352
|
+
resourceType: "service",
|
|
2353
|
+
action: "ensure",
|
|
2354
|
+
id: `${EDGE_PROJECT}/${EDGE_SERVICE}`,
|
|
2355
|
+
via: "found"
|
|
2356
|
+
});
|
|
2357
|
+
}
|
|
1841
2358
|
if (stack.infra.length > 0) {
|
|
1842
2359
|
ctx.logger.info(`\n🗄️ Starting ${stack.infra.join(", ")}…`);
|
|
1843
2360
|
await deps.docker.up(ref, stack.infra);
|
|
@@ -1862,38 +2379,57 @@ async function prepareBuckets(ctx, run, deps) {
|
|
|
1862
2379
|
if (storage.buckets.length === 0) return;
|
|
1863
2380
|
const port = await deps.docker.port(run.ref, "minio", 9e3);
|
|
1864
2381
|
const client = deps.buckets(port, storage);
|
|
1865
|
-
const created = [...await
|
|
2382
|
+
const created = [...await require_seeds.applyBuckets(client, storage.buckets), ...await require_seeds.applyPolicies(client, storage.policies)].filter((entry) => entry.created).length;
|
|
1866
2383
|
ctx.logger.info(`🪣 Buckets: ${storage.buckets.join(", ")}${created ? ` (${created} change(s))` : ""}`);
|
|
1867
2384
|
}
|
|
1868
|
-
/**
|
|
2385
|
+
/**
|
|
2386
|
+
* Create what the plan names in the stack's Postgres, migrate it, then run
|
|
2387
|
+
* its seeds — every one, every time, as each construct's owner.
|
|
2388
|
+
*/
|
|
1869
2389
|
async function prepareDatabases(ctx, run, deps) {
|
|
1870
2390
|
const { stack } = run;
|
|
1871
2391
|
const workspace = ctx.workspace;
|
|
1872
2392
|
const port = await deps.docker.port(run.ref, "postgres", 5432);
|
|
1873
|
-
const statements =
|
|
2393
|
+
const statements = require_seeds.postgresStatements(stack.plan, workspace.name, stack.credential.seed);
|
|
1874
2394
|
if (statements.length > 0) {
|
|
1875
2395
|
ctx.logger.info("🗄️ Creating databases, roles and grants…");
|
|
1876
|
-
await
|
|
2396
|
+
await require_seeds.applyPostgres(deps.sql(port, stack.credential.master), statements);
|
|
1877
2397
|
}
|
|
1878
2398
|
const sources = {};
|
|
1879
2399
|
const manifest = await require_discover.discover({
|
|
1880
|
-
patterns:
|
|
2400
|
+
patterns: require_seeds.constructGlobs(workspace),
|
|
1881
2401
|
cwd: workspace.root,
|
|
1882
2402
|
sources
|
|
1883
2403
|
});
|
|
1884
|
-
const env =
|
|
1885
|
-
ports: { [
|
|
2404
|
+
const env = require_seeds.envFor(stack.plan, {
|
|
2405
|
+
ports: { [require_seeds.primaryPortKey("postgres")]: port },
|
|
1886
2406
|
project: workspace.name,
|
|
1887
2407
|
master: stack.credential.master,
|
|
1888
2408
|
...stack.credential.seed ? { seed: stack.credential.seed } : {}
|
|
1889
2409
|
});
|
|
1890
|
-
const
|
|
2410
|
+
const options = {
|
|
1891
2411
|
root: workspace.root,
|
|
1892
2412
|
manifest,
|
|
1893
2413
|
sources,
|
|
1894
2414
|
env
|
|
2415
|
+
};
|
|
2416
|
+
const runs = await deps.migrate(options);
|
|
2417
|
+
const seeds = await deps.seed({
|
|
2418
|
+
...options,
|
|
2419
|
+
stage: ctx.stage
|
|
2420
|
+
}).catch((error) => {
|
|
2421
|
+
if (error instanceof require_databases.SeedFailed) throw new require_seeds.DeploySeedsFailed(ctx.stage, error.construct, error.seed, error);
|
|
2422
|
+
throw error;
|
|
1895
2423
|
});
|
|
1896
|
-
|
|
2424
|
+
require_seeds.reportDatabaseRuns(runs.map(({ target, applied }) => ({
|
|
2425
|
+
construct: target.id,
|
|
2426
|
+
folder: target.migrations,
|
|
2427
|
+
applied
|
|
2428
|
+
})), seeds.map(({ target, seeded }) => ({
|
|
2429
|
+
construct: target.id,
|
|
2430
|
+
folder: target.seeds,
|
|
2431
|
+
seeded
|
|
2432
|
+
})), (line) => ctx.logger.info(line), (event) => ctx.emit(event));
|
|
1897
2433
|
}
|
|
1898
2434
|
/**
|
|
1899
2435
|
* Each app's image: built from this checkout — inside Docker, every one of
|
|
@@ -1947,6 +2483,12 @@ async function releaseCompose(ctx, run, deps) {
|
|
|
1947
2483
|
if (run.push) return;
|
|
1948
2484
|
ctx.logger.info("\n🚀 Starting the stack…");
|
|
1949
2485
|
await deps.docker.up(ref);
|
|
2486
|
+
const edge = edgeDir(deps.env);
|
|
2487
|
+
if (stack.proxy === "traefik" && stack.traefik) {
|
|
2488
|
+
const written = await writeEdgeRoutes(edge, stack.project, stack.traefik, stack.tls);
|
|
2489
|
+
run.files.push(...written);
|
|
2490
|
+
ctx.logger.info(`🌐 Registered ${stack.routes.length} route(s) with ${EDGE_PROJECT}: ${written.at(-1)}`);
|
|
2491
|
+
} else await removeEdgeRoutes(edge, stack.project);
|
|
1950
2492
|
if (stack.local) await deps.docker.copyOut(ref, "caddy", "/data/caddy/pki/authorities/local/root.crt", caFile(run)).catch(() => {});
|
|
1951
2493
|
await recordImages(ctx, run);
|
|
1952
2494
|
for (const app of stack.apps) {
|
|
@@ -1984,13 +2526,13 @@ async function releaseCompose(ctx, run, deps) {
|
|
|
1984
2526
|
* the tag resolved to.
|
|
1985
2527
|
*/
|
|
1986
2528
|
async function recordImages(ctx, run) {
|
|
1987
|
-
const journal = await
|
|
2529
|
+
const journal = await require_seeds.DeployJournal.open(ctx.state, ctx.stage, () => require_seeds.createEmptyState(ctx.stage, "", ""));
|
|
1988
2530
|
for (const app of [...run.stack.apps, ...run.stack.workers]) {
|
|
1989
2531
|
const image = run.images[app.app] ?? {
|
|
1990
2532
|
ref: app.ref,
|
|
1991
2533
|
tag: app.tag
|
|
1992
2534
|
};
|
|
1993
|
-
|
|
2535
|
+
require_seeds.recordRelease(journal.state, app.app, image);
|
|
1994
2536
|
}
|
|
1995
2537
|
journal.state.identity = ctx.identity.key;
|
|
1996
2538
|
await journal.save();
|
|
@@ -2005,10 +2547,14 @@ async function verifyCompose(ctx, run, deps) {
|
|
|
2005
2547
|
const { stack } = run;
|
|
2006
2548
|
if (run.push) return;
|
|
2007
2549
|
const ca = stack.local ? await (0, node_fs_promises.readFile)(caFile(run), "utf-8").catch(() => void 0) : void 0;
|
|
2008
|
-
ctx.logger.info(
|
|
2550
|
+
ctx.logger.info(`\n🩺 Checking each app through ${stack.proxy === "caddy" ? "Caddy" : "the shared edge"}…`);
|
|
2551
|
+
const edge = stack.local || stack.proxy === "traefik" ? {
|
|
2552
|
+
connectTo: "127.0.0.1",
|
|
2553
|
+
...stack.local ? {} : { connectPort: edgePorts(deps.env).https }
|
|
2554
|
+
} : void 0;
|
|
2009
2555
|
const results = await Promise.all(stack.apps.map((app) => checkApp(ctx, app, deps, {
|
|
2010
2556
|
ca,
|
|
2011
|
-
|
|
2557
|
+
edge
|
|
2012
2558
|
})));
|
|
2013
2559
|
results.push(...await Promise.all(stack.workers.map((worker) => checkContainer(ctx, run, deps, worker.name))));
|
|
2014
2560
|
if (stack.logs) results.push(await checkContainer(ctx, run, deps, LOGS_SERVICE));
|
|
@@ -2017,7 +2563,7 @@ async function verifyCompose(ctx, run, deps) {
|
|
|
2017
2563
|
url,
|
|
2018
2564
|
last
|
|
2019
2565
|
}));
|
|
2020
|
-
if (down.length > 0) throw new ComposeAppsUnhealthy(stack.project, down);
|
|
2566
|
+
if (down.length > 0) throw new ComposeAppsUnhealthy(stack.project, down, stack.proxy);
|
|
2021
2567
|
if (stack.logs) reportLogs(ctx, stack.logs, stack.local);
|
|
2022
2568
|
}
|
|
2023
2569
|
/**
|
|
@@ -2096,7 +2642,7 @@ async function checkApp(ctx, app, deps, options) {
|
|
|
2096
2642
|
status = await deps.probe({
|
|
2097
2643
|
url,
|
|
2098
2644
|
...options.ca ? { ca: options.ca } : {},
|
|
2099
|
-
...options.
|
|
2645
|
+
...options.edge,
|
|
2100
2646
|
timeoutMs: 1e4,
|
|
2101
2647
|
signal: ctx.signal
|
|
2102
2648
|
});
|
|
@@ -2197,19 +2743,34 @@ async function writeStack(root, dir, stack) {
|
|
|
2197
2743
|
files.push(path);
|
|
2198
2744
|
};
|
|
2199
2745
|
await write((0, node_path.join)(dir, "docker-compose.yml"), composeYaml(stack));
|
|
2200
|
-
|
|
2746
|
+
if (stack.caddyfile !== void 0) {
|
|
2747
|
+
await write((0, node_path.join)(dir, "Caddyfile"), stack.caddyfile);
|
|
2748
|
+
await (0, node_fs_promises.rm)((0, node_path.join)(dir, "traefik.yml"), { force: true });
|
|
2749
|
+
} else await (0, node_fs_promises.rm)((0, node_path.join)(dir, "Caddyfile"), { force: true });
|
|
2750
|
+
if (stack.traefik !== void 0) await write((0, node_path.join)(dir, "traefik.yml"), stack.traefik);
|
|
2751
|
+
if (stack.proxy === "caddy" && stack.tls) {
|
|
2752
|
+
await (0, node_fs_promises.mkdir)((0, node_path.join)(dir, "tls"), {
|
|
2753
|
+
recursive: true,
|
|
2754
|
+
mode: 448
|
|
2755
|
+
});
|
|
2756
|
+
await write((0, node_path.join)(dir, "tls", "cert.pem"), await (0, node_fs_promises.readFile)(stack.tls.certFile, "utf-8"));
|
|
2757
|
+
await write((0, node_path.join)(dir, "tls", "key.pem"), await (0, node_fs_promises.readFile)(stack.tls.keyFile, "utf-8"), 384);
|
|
2758
|
+
} else await (0, node_fs_promises.rm)((0, node_path.join)(dir, "tls"), {
|
|
2759
|
+
recursive: true,
|
|
2760
|
+
force: true
|
|
2761
|
+
});
|
|
2201
2762
|
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);
|
|
2202
2763
|
if (stack.logs && stack.compose.services["openobserve"]?.env_file) await write((0, node_path.join)(dir, `${LOGS_SERVICE}.env`), envFile(stack.logs.env), 384);
|
|
2203
2764
|
if (stack.redis && stack.compose.services["redis"]?.env_file) await write((0, node_path.join)(dir, `${REDIS_SERVICE}.env`), envFile(stack.redis.env), 384);
|
|
2204
2765
|
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));
|
|
2205
2766
|
for (const [path, content] of Object.entries(stack.dockerfiles)) await write((0, node_path.join)(root, path), content);
|
|
2206
|
-
await
|
|
2767
|
+
await require_seeds.ensureDockerignore(require_seeds.findBuildRoot(root));
|
|
2207
2768
|
return files;
|
|
2208
2769
|
}
|
|
2209
2770
|
function composeYaml(stack) {
|
|
2210
2771
|
const stage = ` --stage ${stack.stage}`;
|
|
2211
2772
|
return `# Generated by gkm compose from the construct manifest — do not edit.
|
|
2212
|
-
# The ${stack.stage} stage's APIs and sites behind one Caddy, and its workers.
|
|
2773
|
+
# The ${stack.stage} stage's APIs and sites behind ${stack.proxy === "caddy" ? "one Caddy" : `the shared Traefik edge (${EDGE_PROJECT})`}, and its workers.
|
|
2213
2774
|
# Each backend and worker reads exactly the keys in its own env file beside
|
|
2214
2775
|
# this one.
|
|
2215
2776
|
#
|
|
@@ -2305,6 +2866,12 @@ Object.defineProperty(exports, "ComposePushNeedsBuild", {
|
|
|
2305
2866
|
return ComposePushNeedsBuild;
|
|
2306
2867
|
}
|
|
2307
2868
|
});
|
|
2869
|
+
Object.defineProperty(exports, "EDGE_PROJECT", {
|
|
2870
|
+
enumerable: true,
|
|
2871
|
+
get: function() {
|
|
2872
|
+
return EDGE_PROJECT;
|
|
2873
|
+
}
|
|
2874
|
+
});
|
|
2308
2875
|
Object.defineProperty(exports, "composeProject", {
|
|
2309
2876
|
enumerable: true,
|
|
2310
2877
|
get: function() {
|
|
@@ -2329,6 +2896,12 @@ Object.defineProperty(exports, "dockerCompose", {
|
|
|
2329
2896
|
return dockerCompose;
|
|
2330
2897
|
}
|
|
2331
2898
|
});
|
|
2899
|
+
Object.defineProperty(exports, "edgeDir", {
|
|
2900
|
+
enumerable: true,
|
|
2901
|
+
get: function() {
|
|
2902
|
+
return edgeDir;
|
|
2903
|
+
}
|
|
2904
|
+
});
|
|
2332
2905
|
Object.defineProperty(exports, "parseDigests", {
|
|
2333
2906
|
enumerable: true,
|
|
2334
2907
|
get: function() {
|
|
@@ -2341,6 +2914,12 @@ Object.defineProperty(exports, "pinnedRef", {
|
|
|
2341
2914
|
return pinnedRef;
|
|
2342
2915
|
}
|
|
2343
2916
|
});
|
|
2917
|
+
Object.defineProperty(exports, "removeEdgeRoutes", {
|
|
2918
|
+
enumerable: true,
|
|
2919
|
+
get: function() {
|
|
2920
|
+
return removeEdgeRoutes;
|
|
2921
|
+
}
|
|
2922
|
+
});
|
|
2344
2923
|
Object.defineProperty(exports, "stackDir", {
|
|
2345
2924
|
enumerable: true,
|
|
2346
2925
|
get: function() {
|
|
@@ -2354,4 +2933,4 @@ Object.defineProperty(exports, "stackOverrideFile", {
|
|
|
2354
2933
|
}
|
|
2355
2934
|
});
|
|
2356
2935
|
|
|
2357
|
-
//# sourceMappingURL=compose-
|
|
2936
|
+
//# sourceMappingURL=compose-CtUP07r3.cjs.map
|