@geekmidas/cli 10.0.0-alpha.120 → 10.0.0-alpha.122
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/{GoDaddyProvider-3C8U_tVu.cjs → GoDaddyProvider-BJ9j9mR7.cjs} +2 -2
- package/dist/{GoDaddyProvider-3C8U_tVu.cjs.map → GoDaddyProvider-BJ9j9mR7.cjs.map} +1 -1
- package/dist/{GoDaddyProvider-D0MQoOM4.mjs → GoDaddyProvider-wIKYdm7V.mjs} +2 -2
- package/dist/{GoDaddyProvider-D0MQoOM4.mjs.map → GoDaddyProvider-wIKYdm7V.mjs.map} +1 -1
- package/dist/{LegacyStateStore-CRi7TosX.mjs → LegacyStateStore-CkXUJP7i.mjs} +3 -3
- package/dist/{LegacyStateStore-CRi7TosX.mjs.map → LegacyStateStore-CkXUJP7i.mjs.map} +1 -1
- package/dist/{LegacyStateStore-DHsoQGOo.cjs → LegacyStateStore-D1S87I_B.cjs} +3 -3
- package/dist/{LegacyStateStore-DHsoQGOo.cjs.map → LegacyStateStore-D1S87I_B.cjs.map} +1 -1
- package/dist/{LocalStateStore-Dkth0jXX.mjs → LocalStateStore-BWoWeSq0.mjs} +2 -2
- package/dist/{LocalStateStore-Dkth0jXX.mjs.map → LocalStateStore-BWoWeSq0.mjs.map} +1 -1
- package/dist/{LocalStateStore-BQXTkeoS.cjs → LocalStateStore-DmXfpvu4.cjs} +2 -2
- package/dist/{LocalStateStore-BQXTkeoS.cjs.map → LocalStateStore-DmXfpvu4.cjs.map} +1 -1
- package/dist/{S3StateStore-DGKZgyKz.cjs → S3StateStore-BEdsjjP4.cjs} +2 -2
- package/dist/{S3StateStore-DGKZgyKz.cjs.map → S3StateStore-BEdsjjP4.cjs.map} +1 -1
- package/dist/{S3StateStore-pZZnLLBd.mjs → S3StateStore-D19JSGE9.mjs} +2 -2
- package/dist/{S3StateStore-pZZnLLBd.mjs.map → S3StateStore-D19JSGE9.mjs.map} +1 -1
- package/dist/{SSMStateStore-DzkyjQ5D.cjs → SSMStateStore--2bcxoiH.cjs} +2 -2
- package/dist/{SSMStateStore-DzkyjQ5D.cjs.map → SSMStateStore--2bcxoiH.cjs.map} +1 -1
- package/dist/{SSMStateStore-CZRMERDA.mjs → SSMStateStore-7xKw0Zz9.mjs} +2 -2
- package/dist/{SSMStateStore-CZRMERDA.mjs.map → SSMStateStore-7xKw0Zz9.mjs.map} +1 -1
- package/dist/{add-C0kXOsWS.mjs → add-Bu6FkaTn.mjs} +3 -3
- package/dist/{add-C0kXOsWS.mjs.map → add-Bu6FkaTn.mjs.map} +1 -1
- package/dist/{add-Df8cmz0H.cjs → add-DlFUKdjX.cjs} +3 -3
- package/dist/{add-Df8cmz0H.cjs.map → add-DlFUKdjX.cjs.map} +1 -1
- package/dist/{bundler-Dx_grUmc.cjs → bundler-Bg0lPDoi.cjs} +5 -5
- package/dist/{bundler-Dx_grUmc.cjs.map → bundler-Bg0lPDoi.cjs.map} +1 -1
- package/dist/{bundler-B-I4Du99.mjs → bundler-CgE5XTyr.mjs} +5 -5
- package/dist/{bundler-B-I4Du99.mjs.map → bundler-CgE5XTyr.mjs.map} +1 -1
- package/dist/{cli-Cd9exatJ.cjs → cli-Cvjlnpon.cjs} +2 -2
- package/dist/{cli-Cd9exatJ.cjs.map → cli-Cvjlnpon.cjs.map} +1 -1
- package/dist/{cli-DL7GxGAe.mjs → cli-z5S3bHyK.mjs} +2 -2
- package/dist/{cli-DL7GxGAe.mjs.map → cli-z5S3bHyK.mjs.map} +1 -1
- package/dist/{compose-C43c03WZ.mjs → compose-CgBPA44v.mjs} +28 -5
- package/dist/{compose-C43c03WZ.mjs.map → compose-CgBPA44v.mjs.map} +1 -1
- package/dist/{compose-Mdgea4Yt.cjs → compose-bKLAtHLj.cjs} +28 -5
- package/dist/compose-bKLAtHLj.cjs.map +1 -0
- package/dist/deploy/public.cjs +6 -6
- package/dist/deploy/public.mjs +6 -6
- package/dist/{deploy-BMM2hx-K.cjs → deploy-CI85F6dr.cjs} +5 -5
- package/dist/{deploy-BMM2hx-K.cjs.map → deploy-CI85F6dr.cjs.map} +1 -1
- package/dist/{deploy-D5YhwDng.mjs → deploy-Ddy8UBoE.mjs} +5 -5
- package/dist/{deploy-D5YhwDng.mjs.map → deploy-Ddy8UBoE.mjs.map} +1 -1
- package/dist/{dns-agf2VCdV.mjs → dns-BgxvvcQa.mjs} +164 -26
- package/dist/dns-BgxvvcQa.mjs.map +1 -0
- package/dist/{dns-BIYf20p7.cjs → dns-CfEZHt7r.cjs} +164 -26
- package/dist/dns-CfEZHt7r.cjs.map +1 -0
- package/dist/{dokploy-2p4PK6ej.mjs → dokploy-Boj1NSHV.mjs} +2 -2
- package/dist/{dokploy-2p4PK6ej.mjs.map → dokploy-Boj1NSHV.mjs.map} +1 -1
- package/dist/{dokploy-BANMTroy.cjs → dokploy-CiozgdKO.cjs} +2 -2
- package/dist/{dokploy-BANMTroy.cjs.map → dokploy-CiozgdKO.cjs.map} +1 -1
- package/dist/{eas-CNi16SoO.cjs → eas-BmBjIFf_.cjs} +2 -2
- package/dist/{eas-CNi16SoO.cjs.map → eas-BmBjIFf_.cjs.map} +1 -1
- package/dist/{eas-iT2JDHCh.mjs → eas-DCg41jKK.mjs} +2 -2
- package/dist/{eas-iT2JDHCh.mjs.map → eas-DCg41jKK.mjs.map} +1 -1
- package/dist/{encryption-d2lLtkYx.cjs → encryption-CwLWV2ig.cjs} +2 -2
- package/dist/{encryption-d2lLtkYx.cjs.map → encryption-CwLWV2ig.cjs.map} +1 -1
- package/dist/{encryption-DkXVbyZc.mjs → encryption-QOQbGXgV.mjs} +2 -2
- package/dist/{encryption-DkXVbyZc.mjs.map → encryption-QOQbGXgV.mjs.map} +1 -1
- package/dist/{engine-DlEHexED.cjs → engine-D7YNY3v7.cjs} +3 -3
- package/dist/{engine-DlEHexED.cjs.map → engine-D7YNY3v7.cjs.map} +1 -1
- package/dist/{engine-CatKW6E-.mjs → engine-HVqo3mQu.mjs} +3 -3
- package/dist/{engine-CatKW6E-.mjs.map → engine-HVqo3mQu.mjs.map} +1 -1
- package/dist/{github-BAeaC1Pl.cjs → github-CO8W9SDs.cjs} +4 -4
- package/dist/{github-BAeaC1Pl.cjs.map → github-CO8W9SDs.cjs.map} +1 -1
- package/dist/{github-DErsqKGN.mjs → github-CQt3xc3o.mjs} +4 -4
- package/dist/{github-DErsqKGN.mjs.map → github-CQt3xc3o.mjs.map} +1 -1
- package/dist/{googlePlay-BXJ5ZjMK.mjs → googlePlay-Cfu17aEp.mjs} +3 -3
- package/dist/{googlePlay-BXJ5ZjMK.mjs.map → googlePlay-Cfu17aEp.mjs.map} +1 -1
- package/dist/{googlePlay-BVcfFWRO.cjs → googlePlay-DSuF6jbF.cjs} +3 -3
- package/dist/{googlePlay-BVcfFWRO.cjs.map → googlePlay-DSuF6jbF.cjs.map} +1 -1
- package/dist/index.cjs +51 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +51 -51
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-s4QizIoM.mjs → migrate-Bkz8BGaZ.mjs} +2 -2
- package/dist/{migrate-s4QizIoM.mjs.map → migrate-Bkz8BGaZ.mjs.map} +1 -1
- package/dist/{migrate-Bb1MmGHl.mjs → migrate-CGkir5LM.mjs} +2 -2
- package/dist/{migrate-Bb1MmGHl.mjs.map → migrate-CGkir5LM.mjs.map} +1 -1
- package/dist/{migrate-L4a273dY.cjs → migrate-CXsMaHbb.cjs} +2 -2
- package/dist/{migrate-L4a273dY.cjs.map → migrate-CXsMaHbb.cjs.map} +1 -1
- package/dist/{migrate-D79GI0XM.cjs → migrate-dbAI2XF8.cjs} +2 -2
- package/dist/{migrate-D79GI0XM.cjs.map → migrate-dbAI2XF8.cjs.map} +1 -1
- package/dist/{publish-ydscCYsW.cjs → publish-Bbd-2PI2.cjs} +2 -2
- package/dist/{publish-ydscCYsW.cjs.map → publish-Bbd-2PI2.cjs.map} +1 -1
- package/dist/{publish-CcTlmJT6.mjs → publish-eUcWvSdP.mjs} +2 -2
- package/dist/{publish-CcTlmJT6.mjs.map → publish-eUcWvSdP.mjs.map} +1 -1
- package/dist/{resolve-BgL-brF9.cjs → resolve-CyprRcQo.cjs} +3 -3
- package/dist/{resolve-BgL-brF9.cjs.map → resolve-CyprRcQo.cjs.map} +1 -1
- package/dist/{resolve-BLp2trep.mjs → resolve-DSilYpzm.mjs} +3 -3
- package/dist/{resolve-BLp2trep.mjs.map → resolve-DSilYpzm.mjs.map} +1 -1
- package/dist/{rollback-DwqZ8zzj.mjs → rollback-C4ri3h7i.mjs} +4 -4
- package/dist/{rollback-DwqZ8zzj.mjs.map → rollback-C4ri3h7i.mjs.map} +1 -1
- package/dist/{rollback-2cyLXEH1.cjs → rollback-DfwsnwGO.cjs} +4 -4
- package/dist/{rollback-2cyLXEH1.cjs.map → rollback-DfwsnwGO.cjs.map} +1 -1
- package/dist/{seeds-C2w4UqVP.cjs → seeds-CFG-f24B.cjs} +9 -3
- package/dist/{seeds-C2w4UqVP.cjs.map → seeds-CFG-f24B.cjs.map} +1 -1
- package/dist/{seeds-Cwtv-U9-.mjs → seeds-oY0qDqui.mjs} +4 -4
- package/dist/{seeds-Cwtv-U9-.mjs.map → seeds-oY0qDqui.mjs.map} +1 -1
- package/dist/{serverSetup-CKs4K9gD.cjs → serverSetup-CQZt0OT6.cjs} +6 -5
- package/dist/serverSetup-CQZt0OT6.cjs.map +1 -0
- package/dist/{serverSetup-P68H5Lal.mjs → serverSetup-D8nngcNL.mjs} +6 -5
- package/dist/serverSetup-D8nngcNL.mjs.map +1 -0
- package/dist/{sniffer-CF7wNxMo.cjs → sniffer-BAmtXzou.cjs} +6 -6
- package/dist/{sniffer-CF7wNxMo.cjs.map → sniffer-BAmtXzou.cjs.map} +1 -1
- package/dist/{sniffer-8C4q6Szd.mjs → sniffer-Cgx608Tl.mjs} +6 -6
- package/dist/{sniffer-8C4q6Szd.mjs.map → sniffer-Cgx608Tl.mjs.map} +1 -1
- package/dist/{stage-BvM_u2z4.mjs → stage-BKDdQm65.mjs} +4 -4
- package/dist/{stage-BvM_u2z4.mjs.map → stage-BKDdQm65.mjs.map} +1 -1
- package/dist/{stage-DEQJGW3r.cjs → stage-CJysBXWB.cjs} +4 -4
- package/dist/{stage-DEQJGW3r.cjs.map → stage-CJysBXWB.cjs.map} +1 -1
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.mjs +1 -1
- package/package.json +15 -15
- package/dist/compose-Mdgea4Yt.cjs.map +0 -1
- package/dist/dns-BIYf20p7.cjs.map +0 -1
- package/dist/dns-agf2VCdV.mjs.map +0 -1
- package/dist/serverSetup-CKs4K9gD.cjs.map +0 -1
- package/dist/serverSetup-P68H5Lal.mjs.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
2
2
|
import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
|
|
3
|
-
import { C as dockerCompose, a as edgePorts, b as dockerHost, d as composeStack, g as edgeAlias, s as deployedStackSecrets, x as sshAccess, y as composeServer } from "./compose-
|
|
3
|
+
import { C as dockerCompose, a as edgePorts, b as dockerHost, d as composeStack, g as edgeAlias, s as deployedStackSecrets, x as sshAccess, y as composeServer } from "./compose-CgBPA44v.mjs";
|
|
4
4
|
import { N as assertDeployedStage } from "./workspace-D1QbNd55.mjs";
|
|
5
|
-
import { S as deploysWithCompose } from "./dns-
|
|
6
|
-
import {
|
|
5
|
+
import { S as deploysWithCompose } from "./dns-BgxvvcQa.mjs";
|
|
6
|
+
import { Lt as secretsStoreFor, ft as constructGlobs } from "./seeds-oY0qDqui.mjs";
|
|
7
7
|
import { n as deployIdentity } from "./identity-CBXr4wNi.mjs";
|
|
8
8
|
import { i as discover } from "./discover-C5roJuc9.mjs";
|
|
9
9
|
import { NotInAnApp, loadWorkspaceAppInfo, loadWorkspaceConfig } from "./config.mjs";
|
|
@@ -299,4 +299,4 @@ function run(commandArgs, credentials, cwd) {
|
|
|
299
299
|
//#endregion
|
|
300
300
|
export { stage_exports as n, loadStage as t };
|
|
301
301
|
|
|
302
|
-
//# sourceMappingURL=stage-
|
|
302
|
+
//# sourceMappingURL=stage-BKDdQm65.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage-BvM_u2z4.mjs","names":[],"sources":["../src/exec/stage.ts"],"sourcesContent":["/**\n * `gkm exec --stage <stage> -- <command>`: a command run on this machine as\n * a deployed compose stage's app — a data import, a backfill, a report.\n *\n * The app's environment is the one its container runs with, derived the way\n * the deploy derives it (`composeStack`) from the stage's secrets, read from\n * the stage's store the way the deploy reads them. Only what the container\n * reaches on the stack's network is moved: each service it names that the\n * server publishes on its loopback — Postgres, MinIO — is reached through an\n * SSH tunnel on the deploy's own login (`ServerAccess.tunnel`), and each\n * sibling app through its public address.\n *\n * Nothing is written to disk. The values exist only in the command's\n * environment, and its Credentials are filled from there by a preload that\n * is a `data:` URL naming the keys — never their values.\n */\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { type ComposeDocker, dockerCompose } from '../compose/docker.js';\nimport { deployedStackSecrets } from '../compose/secrets.js';\nimport {\n\ttype ComposeServer,\n\tcomposeServer,\n\tdockerHost,\n\ttype ServerAccess,\n\tsshAccess,\n\ttype Tunnel,\n} from '../compose/server.js';\nimport { type ComposeStack, composeStack } from '../compose/stack.js';\nimport { edgeAlias } from '../compose/traefik.js';\nimport {\n\tloadWorkspaceAppInfo,\n\tloadWorkspaceConfig,\n\tNotInAnApp,\n} from '../config.js';\nimport { deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\nimport { prompt } from '../prompt.js';\nimport { deploysWithCompose } from '../providers/dns.js';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\n/** The stage is not one the compose target deploys. */\nexport class ExecStageNotComposed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`gkm exec --stage runs a command against a compose stage's server, and ` +\n\t\t\t\t`'${stage}' is not deployed with compose. Set deploy.default: 'compose' ` +\n\t\t\t\t'in gkm.config.ts, or run the command where the stage runs.',\n\t\t);\n\t\tthis.name = 'ExecStageNotComposed';\n\t}\n}\n\n/** The stage's secrets lack what its deploy generates: it was never deployed. */\nexport class ExecStageNotDeployed extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly missing: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${missing.join(', ')} in its secrets yet, so what its ` +\n\t\t\t\t'apps run with is not decided. Deploy it first: ' +\n\t\t\t\t`gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'ExecStageNotDeployed';\n\t}\n}\n\n/** The directory is not one of the stack's apps. */\nexport class ExecAppNotInStack extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly app: string | undefined,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${app ? `'${app}' is not` : 'This directory is not in'} one of the apps ` +\n\t\t\t\t`'${stage}' runs (${apps.join(', ')}), so there is no environment to run ` +\n\t\t\t\t`as. Run from an app's directory: cd apps/${apps[0] ?? '<app>'} && ` +\n\t\t\t\t`gkm exec --stage ${stage} -- <command>.`,\n\t\t);\n\t\tthis.name = 'ExecAppNotInStack';\n\t}\n}\n\n/** A protected stage, and nobody said yes. */\nexport class ExecNotConfirmed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(`Nothing was run against '${stage}'.`);\n\t\tthis.name = 'ExecNotConfirmed';\n\t}\n}\n\nexport interface StageExecOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\t/**\n\t * The SSH login to reach the server as, in place of\n\t * `deploy.compose.server.<stage>.user` — a person's own, where the\n\t * deploy's key lives only in CI.\n\t */\n\tsshUser?: string;\n\t/** Run against a protected stage without asking. */\n\tyes?: boolean;\n\tcwd?: string;\n}\n\n/** What the command needs from outside the process, so tests can stand in. */\nexport interface StageExecDeps {\n\t/** The server's Docker, asked which port each service publishes. */\n\tdocker: ComposeDocker;\n\t/** The tunnels to the server's loopback. */\n\tserver: ServerAccess;\n\t/** Asked before running against a protected stage. */\n\tconfirm: (question: string) => Promise<boolean>;\n\t/** Where the edge's ports are read from, as the deploy reads them. */\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport const defaultStageExecDeps: StageExecDeps = {\n\tdocker: dockerCompose,\n\tserver: sshAccess,\n\tconfirm: async (question) => {\n\t\tconst answer = await prompt(`${question} [y/N] `, {\n\t\t\tinstead: 'Pass --yes to run without asking.',\n\t\t});\n\t\treturn /^y(es)?$/i.test(answer.trim());\n\t},\n\tenv: process.env,\n};\n\n/** A service on the stack's network the app's environment names. */\nexport interface StackAddress {\n\tservice: string;\n\tport: number;\n}\n\n/**\n * The environment an app runs with on the stage: a backend's container's,\n * over the keys of each worker built from it; a site's or a mobile app's,\n * the public values its build reads.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: Pick<ComposeStack, 'stage' | 'apps' | 'workers' | 'mobileApps'>,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst mobile = stack.mobileApps.find(\n\t\t(candidate) => candidate.name === appName,\n\t);\n\tif (mobile) return { ...mobile.values };\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(stack.stage, appName, [\n\t\t\t...stack.apps.map((candidate) => candidate.name),\n\t\t\t...stack.mobileApps.map((candidate) => candidate.name),\n\t\t]);\n\t}\n\tif (app.kind === 'site') return { ...app.build?.args };\n\tconst workers = stack.workers.filter((worker) => worker.host === app.name);\n\treturn Object.assign(\n\t\t{},\n\t\t...workers.map((worker) => worker.env ?? {}),\n\t\tapp.env ?? {},\n\t);\n}\n\n/** `host:port`, at a URL's authority — after `//` or a login's `@`. */\nfunction authority(host: string, port: number): RegExp {\n\treturn new RegExp(\n\t\t`(?<=[/@,])${host.replace(/[.-]/g, '\\\\$&')}:${port}(?!\\\\d)`,\n\t\t'g',\n\t);\n}\n\n/**\n * Each of the stack's services the environment reaches on the stack's\n * network, once: `postgres:5432`, `minio:9000`.\n */\nexport function stackAddresses(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'infra'>,\n): StackAddress[] {\n\tconst found = new Map<string, StackAddress>();\n\tconst pattern = /(?<=[/@,])([a-z0-9-]+):(\\d+)(?!\\d)/g;\n\tfor (const value of Object.values(env)) {\n\t\tfor (const match of value.matchAll(pattern)) {\n\t\t\tconst service = match[1]!;\n\t\t\tif (!stack.infra.includes(service)) continue;\n\t\t\tconst port = Number(match[2]);\n\t\t\tfound.set(`${service}:${port}`, { service, port });\n\t\t}\n\t}\n\treturn [...found.values()];\n}\n\n/**\n * The environment as this machine reaches it: each service at its tunnel's\n * local port, and each sibling app at its public address. What has no\n * tunnel is left as the container has it.\n */\nexport function throughTunnels(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'apps' | 'project'>,\n\ttunnels: ReadonlyMap<string, number>,\n): Record<string, string> {\n\tconst rewrites: [RegExp, string][] = [];\n\tfor (const [address, port] of tunnels) {\n\t\tconst [service, inside] = address.split(':');\n\t\trewrites.push([authority(service!, Number(inside)), `127.0.0.1:${port}`]);\n\t}\n\tfor (const app of stack.apps) {\n\t\tfor (const host of [app.name, edgeAlias(stack.project, app.name)]) {\n\t\t\trewrites.push([\n\t\t\t\tnew RegExp(\n\t\t\t\t\t`http://${host.replace(/[.-]/g, '\\\\$&')}:${app.port}(?!\\\\d)`,\n\t\t\t\t\t'g',\n\t\t\t\t),\n\t\t\t\tapp.url,\n\t\t\t]);\n\t\t}\n\t}\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tout[key] = rewrites.reduce(\n\t\t\t(current, [pattern, replacement]) =>\n\t\t\t\tcurrent.replace(pattern, () => replacement),\n\t\t\tvalue,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * The preload that fills Credentials from the command's own environment: a\n * `data:` URL holding the keys, so no file — and no value — is written.\n */\nexport function credentialsImport(keys: readonly string[]): string {\n\tconst source =\n\t\t'globalThis.__gkm_credentials__=Object.fromEntries(' +\n\t\t`${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;\n\treturn `--import=data:text/javascript,${encodeURIComponent(source)}`;\n}\n\n/** A deployed compose stage, as its deploy has it, and the app here. */\nexport interface LoadedStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tstack: ComposeStack;\n\t/** The stage's secrets, as its deploy reads them. */\n\tsecrets: StageSecrets;\n\t/** The app this directory is, if it is one. */\n\tappName: string | undefined;\n\tprotectedStage: boolean;\n}\n\n/**\n * The stage's stack, and the app this directory is, as the deploy has them:\n * its secrets read from its store, its values derived as the deploy derives\n * them. Nothing is reached but the store — no server, no SSH.\n *\n * @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}\n */\nexport async function loadStage(\n\toptions: Pick<StageExecOptions, 'stage' | 'profile' | 'cwd'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<LoadedStage> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst { stage } = options;\n\t// Outside an app the workspace is still loaded, so the stage is checked\n\t// before the directory is refused.\n\tconst info = await loadWorkspaceAppInfo(cwd).catch((error) => {\n\t\tif (error instanceof NotInAnApp) return undefined;\n\t\tthrow error;\n\t});\n\tconst workspace =\n\t\tinfo?.workspace ?? (await loadWorkspaceConfig(cwd)).workspace;\n\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!deploysWithCompose(workspace)) throw new ExecStageNotComposed(stage);\n\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest: ConstructManifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\n\tconst store = await secretsStoreFor(workspace, stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { secrets, generated } = deployedStackSecrets(\n\t\tworkspace,\n\t\tstage,\n\t\tawait store.read(stage),\n\t\tmanifest,\n\t);\n\tif (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);\n\n\tconst stack = composeStack({\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\tstage,\n\t\tidentity: deployIdentity(workspace, stage),\n\t\t// Built, so a site's public values are resolved as its build has them.\n\t\timages: { mode: 'build', tag: 'exec' },\n\t\tsecrets,\n\t\tports: edgePorts(env),\n\t});\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tstack,\n\t\tsecrets,\n\t\tappName: info?.appName,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\n}\n\n/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */\nfunction stageServer(\n\tloaded: LoadedStage,\n\toptions: StageExecOptions,\n): ComposeServer {\n\tconst server = composeServer({\n\t\tstage: options.stage,\n\t\tlocal: false,\n\t\tconfig: loaded.workspace.deploy?.compose?.server,\n\t\tcustom: loaded.secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn server;\n}\n\n/**\n * Run `commandArgs` as the app this directory is, on the stage — through a\n * tunnel to each service it reaches — and return its exit code. Every tunnel\n * is closed once it exits, whatever it exits with.\n *\n * @throws {ExecStageNotComposed}, {ExecStageNotDeployed}, {ExecAppNotInStack},\n * {ExecNotConfirmed}, {ComposeServerUnreachable}\n */\nexport async function execOnStage(\n\tcommandArgs: readonly [string, ...string[]],\n\toptions: StageExecOptions,\n\tdeps: StageExecDeps = defaultStageExecDeps,\n): Promise<number> {\n\tconst { stage } = options;\n\tconst loaded = await loadStage(options, deps.env);\n\tconst { stack, appName, protectedStage } = loaded;\n\tconst env = appEnvironment(stack, appName);\n\n\tif (protectedStage && !options.yes) {\n\t\tconst question = `'${stage}' is a protected stage. Run ${commandArgs.join(' ')} against it?`;\n\t\tif (!(await deps.confirm(question))) throw new ExecNotConfirmed(stage);\n\t}\n\n\t// Only what reaches a service on the stack needs the server: a site's or\n\t// a mobile app's values are public addresses, and need no SSH at all.\n\tconst addresses = stackAddresses(env, stack);\n\tif (addresses.length === 0) {\n\t\tlogger.log(`📦 App: ${appName} on '${stage}'`);\n\t\tlogger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);\n\t\treturn run(commandArgs, env, options.cwd);\n\t}\n\n\tconst server = stageServer(loaded, options);\n\tlogger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);\n\n\t// The deploy's own check: SSH logs in and Docker answers, before any\n\t// tunnel is asked for — so a key the server refuses is said as that.\n\tawait deps.server.check(server);\n\n\tconst tunnels: Tunnel[] = [];\n\tconst ports = new Map<string, number>();\n\ttry {\n\t\tfor (const address of addresses) {\n\t\t\tconst published = await deps.docker.published(\n\t\t\t\t{ host: dockerHost(server) },\n\t\t\t\tstack.project,\n\t\t\t\taddress.service,\n\t\t\t\taddress.port,\n\t\t\t);\n\t\t\tif (published === undefined) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`⚠️ ${address.service} is not published on the server (no running ` +\n\t\t\t\t\t\t`${stack.project} ${address.service} publishes ${address.port}), so ` +\n\t\t\t\t\t\t'it is unreachable from here; its address is passed on unchanged.',\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst tunnel = await deps.server.tunnel(server, published);\n\t\t\ttunnels.push(tunnel);\n\t\t\tports.set(`${address.service}:${address.port}`, tunnel.port);\n\t\t\tlogger.log(`🚇 ${address.service} → 127.0.0.1:${tunnel.port} (over SSH)`);\n\t\t}\n\n\t\tconst credentials = throughTunnels(env, stack, ports);\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length} value(s) for '${stage}'`,\n\t\t);\n\t\treturn await run(commandArgs, credentials, options.cwd);\n\t} finally {\n\t\tawait Promise.all(tunnels.map((tunnel) => tunnel.close()));\n\t}\n}\n\n/**\n * The command, with the values in its environment and its Credentials. A\n * Ctrl-C reaches it — it is in the terminal's process group — and this\n * process waits for it to exit rather than leaving before the tunnels close.\n */\nfunction run(\n\tcommandArgs: readonly [string, ...string[]],\n\tcredentials: Record<string, string>,\n\tcwd: string | undefined,\n): Promise<number> {\n\tconst [cmd, ...args] = commandArgs;\n\tlogger.log(`🚀 Running: ${commandArgs.join(' ')}`);\n\n\tconst inherited = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst nodeOptions = [inherited, credentialsImport(Object.keys(credentials))]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst wait = () => {};\n\tprocess.on('SIGINT', wait);\n\tprocess.on('SIGTERM', wait);\n\treturn new Promise<number>((resolve) => {\n\t\tconst child = spawn(cmd, args, {\n\t\t\t...(cwd ? { cwd } : {}),\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, ...credentials, NODE_OPTIONS: nodeOptions },\n\t\t});\n\t\tchild.on('close', (code, signal) => {\n\t\t\tresolve(code ?? 128 + (signal ? constants.signals[signal] : 0));\n\t\t});\n\t\tchild.on('error', (error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t}).finally(() => {\n\t\tprocess.off('SIGINT', wait);\n\t\tprocess.off('SIGTERM', wait);\n\t});\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stage-BKDdQm65.mjs","names":[],"sources":["../src/exec/stage.ts"],"sourcesContent":["/**\n * `gkm exec --stage <stage> -- <command>`: a command run on this machine as\n * a deployed compose stage's app — a data import, a backfill, a report.\n *\n * The app's environment is the one its container runs with, derived the way\n * the deploy derives it (`composeStack`) from the stage's secrets, read from\n * the stage's store the way the deploy reads them. Only what the container\n * reaches on the stack's network is moved: each service it names that the\n * server publishes on its loopback — Postgres, MinIO — is reached through an\n * SSH tunnel on the deploy's own login (`ServerAccess.tunnel`), and each\n * sibling app through its public address.\n *\n * Nothing is written to disk. The values exist only in the command's\n * environment, and its Credentials are filled from there by a preload that\n * is a `data:` URL naming the keys — never their values.\n */\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { type ComposeDocker, dockerCompose } from '../compose/docker.js';\nimport { deployedStackSecrets } from '../compose/secrets.js';\nimport {\n\ttype ComposeServer,\n\tcomposeServer,\n\tdockerHost,\n\ttype ServerAccess,\n\tsshAccess,\n\ttype Tunnel,\n} from '../compose/server.js';\nimport { type ComposeStack, composeStack } from '../compose/stack.js';\nimport { edgeAlias } from '../compose/traefik.js';\nimport {\n\tloadWorkspaceAppInfo,\n\tloadWorkspaceConfig,\n\tNotInAnApp,\n} from '../config.js';\nimport { deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\nimport { prompt } from '../prompt.js';\nimport { deploysWithCompose } from '../providers/dns.js';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\n/** The stage is not one the compose target deploys. */\nexport class ExecStageNotComposed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`gkm exec --stage runs a command against a compose stage's server, and ` +\n\t\t\t\t`'${stage}' is not deployed with compose. Set deploy.default: 'compose' ` +\n\t\t\t\t'in gkm.config.ts, or run the command where the stage runs.',\n\t\t);\n\t\tthis.name = 'ExecStageNotComposed';\n\t}\n}\n\n/** The stage's secrets lack what its deploy generates: it was never deployed. */\nexport class ExecStageNotDeployed extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly missing: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${missing.join(', ')} in its secrets yet, so what its ` +\n\t\t\t\t'apps run with is not decided. Deploy it first: ' +\n\t\t\t\t`gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'ExecStageNotDeployed';\n\t}\n}\n\n/** The directory is not one of the stack's apps. */\nexport class ExecAppNotInStack extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly app: string | undefined,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${app ? `'${app}' is not` : 'This directory is not in'} one of the apps ` +\n\t\t\t\t`'${stage}' runs (${apps.join(', ')}), so there is no environment to run ` +\n\t\t\t\t`as. Run from an app's directory: cd apps/${apps[0] ?? '<app>'} && ` +\n\t\t\t\t`gkm exec --stage ${stage} -- <command>.`,\n\t\t);\n\t\tthis.name = 'ExecAppNotInStack';\n\t}\n}\n\n/** A protected stage, and nobody said yes. */\nexport class ExecNotConfirmed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(`Nothing was run against '${stage}'.`);\n\t\tthis.name = 'ExecNotConfirmed';\n\t}\n}\n\nexport interface StageExecOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\t/**\n\t * The SSH login to reach the server as, in place of\n\t * `deploy.compose.server.<stage>.user` — a person's own, where the\n\t * deploy's key lives only in CI.\n\t */\n\tsshUser?: string;\n\t/** Run against a protected stage without asking. */\n\tyes?: boolean;\n\tcwd?: string;\n}\n\n/** What the command needs from outside the process, so tests can stand in. */\nexport interface StageExecDeps {\n\t/** The server's Docker, asked which port each service publishes. */\n\tdocker: ComposeDocker;\n\t/** The tunnels to the server's loopback. */\n\tserver: ServerAccess;\n\t/** Asked before running against a protected stage. */\n\tconfirm: (question: string) => Promise<boolean>;\n\t/** Where the edge's ports are read from, as the deploy reads them. */\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport const defaultStageExecDeps: StageExecDeps = {\n\tdocker: dockerCompose,\n\tserver: sshAccess,\n\tconfirm: async (question) => {\n\t\tconst answer = await prompt(`${question} [y/N] `, {\n\t\t\tinstead: 'Pass --yes to run without asking.',\n\t\t});\n\t\treturn /^y(es)?$/i.test(answer.trim());\n\t},\n\tenv: process.env,\n};\n\n/** A service on the stack's network the app's environment names. */\nexport interface StackAddress {\n\tservice: string;\n\tport: number;\n}\n\n/**\n * The environment an app runs with on the stage: a backend's container's,\n * over the keys of each worker built from it; a site's or a mobile app's,\n * the public values its build reads.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: Pick<ComposeStack, 'stage' | 'apps' | 'workers' | 'mobileApps'>,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst mobile = stack.mobileApps.find(\n\t\t(candidate) => candidate.name === appName,\n\t);\n\tif (mobile) return { ...mobile.values };\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(stack.stage, appName, [\n\t\t\t...stack.apps.map((candidate) => candidate.name),\n\t\t\t...stack.mobileApps.map((candidate) => candidate.name),\n\t\t]);\n\t}\n\tif (app.kind === 'site') return { ...app.build?.args };\n\tconst workers = stack.workers.filter((worker) => worker.host === app.name);\n\treturn Object.assign(\n\t\t{},\n\t\t...workers.map((worker) => worker.env ?? {}),\n\t\tapp.env ?? {},\n\t);\n}\n\n/** `host:port`, at a URL's authority — after `//` or a login's `@`. */\nfunction authority(host: string, port: number): RegExp {\n\treturn new RegExp(\n\t\t`(?<=[/@,])${host.replace(/[.-]/g, '\\\\$&')}:${port}(?!\\\\d)`,\n\t\t'g',\n\t);\n}\n\n/**\n * Each of the stack's services the environment reaches on the stack's\n * network, once: `postgres:5432`, `minio:9000`.\n */\nexport function stackAddresses(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'infra'>,\n): StackAddress[] {\n\tconst found = new Map<string, StackAddress>();\n\tconst pattern = /(?<=[/@,])([a-z0-9-]+):(\\d+)(?!\\d)/g;\n\tfor (const value of Object.values(env)) {\n\t\tfor (const match of value.matchAll(pattern)) {\n\t\t\tconst service = match[1]!;\n\t\t\tif (!stack.infra.includes(service)) continue;\n\t\t\tconst port = Number(match[2]);\n\t\t\tfound.set(`${service}:${port}`, { service, port });\n\t\t}\n\t}\n\treturn [...found.values()];\n}\n\n/**\n * The environment as this machine reaches it: each service at its tunnel's\n * local port, and each sibling app at its public address. What has no\n * tunnel is left as the container has it.\n */\nexport function throughTunnels(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'apps' | 'project'>,\n\ttunnels: ReadonlyMap<string, number>,\n): Record<string, string> {\n\tconst rewrites: [RegExp, string][] = [];\n\tfor (const [address, port] of tunnels) {\n\t\tconst [service, inside] = address.split(':');\n\t\trewrites.push([authority(service!, Number(inside)), `127.0.0.1:${port}`]);\n\t}\n\tfor (const app of stack.apps) {\n\t\tfor (const host of [app.name, edgeAlias(stack.project, app.name)]) {\n\t\t\trewrites.push([\n\t\t\t\tnew RegExp(\n\t\t\t\t\t`http://${host.replace(/[.-]/g, '\\\\$&')}:${app.port}(?!\\\\d)`,\n\t\t\t\t\t'g',\n\t\t\t\t),\n\t\t\t\tapp.url,\n\t\t\t]);\n\t\t}\n\t}\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tout[key] = rewrites.reduce(\n\t\t\t(current, [pattern, replacement]) =>\n\t\t\t\tcurrent.replace(pattern, () => replacement),\n\t\t\tvalue,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * The preload that fills Credentials from the command's own environment: a\n * `data:` URL holding the keys, so no file — and no value — is written.\n */\nexport function credentialsImport(keys: readonly string[]): string {\n\tconst source =\n\t\t'globalThis.__gkm_credentials__=Object.fromEntries(' +\n\t\t`${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;\n\treturn `--import=data:text/javascript,${encodeURIComponent(source)}`;\n}\n\n/** A deployed compose stage, as its deploy has it, and the app here. */\nexport interface LoadedStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tstack: ComposeStack;\n\t/** The stage's secrets, as its deploy reads them. */\n\tsecrets: StageSecrets;\n\t/** The app this directory is, if it is one. */\n\tappName: string | undefined;\n\tprotectedStage: boolean;\n}\n\n/**\n * The stage's stack, and the app this directory is, as the deploy has them:\n * its secrets read from its store, its values derived as the deploy derives\n * them. Nothing is reached but the store — no server, no SSH.\n *\n * @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}\n */\nexport async function loadStage(\n\toptions: Pick<StageExecOptions, 'stage' | 'profile' | 'cwd'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<LoadedStage> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst { stage } = options;\n\t// Outside an app the workspace is still loaded, so the stage is checked\n\t// before the directory is refused.\n\tconst info = await loadWorkspaceAppInfo(cwd).catch((error) => {\n\t\tif (error instanceof NotInAnApp) return undefined;\n\t\tthrow error;\n\t});\n\tconst workspace =\n\t\tinfo?.workspace ?? (await loadWorkspaceConfig(cwd)).workspace;\n\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!deploysWithCompose(workspace)) throw new ExecStageNotComposed(stage);\n\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest: ConstructManifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\n\tconst store = await secretsStoreFor(workspace, stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { secrets, generated } = deployedStackSecrets(\n\t\tworkspace,\n\t\tstage,\n\t\tawait store.read(stage),\n\t\tmanifest,\n\t);\n\tif (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);\n\n\tconst stack = composeStack({\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\tstage,\n\t\tidentity: deployIdentity(workspace, stage),\n\t\t// Built, so a site's public values are resolved as its build has them.\n\t\timages: { mode: 'build', tag: 'exec' },\n\t\tsecrets,\n\t\tports: edgePorts(env),\n\t});\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tstack,\n\t\tsecrets,\n\t\tappName: info?.appName,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\n}\n\n/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */\nfunction stageServer(\n\tloaded: LoadedStage,\n\toptions: StageExecOptions,\n): ComposeServer {\n\tconst server = composeServer({\n\t\tstage: options.stage,\n\t\tlocal: false,\n\t\tconfig: loaded.workspace.deploy?.compose?.server,\n\t\tcustom: loaded.secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn server;\n}\n\n/**\n * Run `commandArgs` as the app this directory is, on the stage — through a\n * tunnel to each service it reaches — and return its exit code. Every tunnel\n * is closed once it exits, whatever it exits with.\n *\n * @throws {ExecStageNotComposed}, {ExecStageNotDeployed}, {ExecAppNotInStack},\n * {ExecNotConfirmed}, {ComposeServerUnreachable}\n */\nexport async function execOnStage(\n\tcommandArgs: readonly [string, ...string[]],\n\toptions: StageExecOptions,\n\tdeps: StageExecDeps = defaultStageExecDeps,\n): Promise<number> {\n\tconst { stage } = options;\n\tconst loaded = await loadStage(options, deps.env);\n\tconst { stack, appName, protectedStage } = loaded;\n\tconst env = appEnvironment(stack, appName);\n\n\tif (protectedStage && !options.yes) {\n\t\tconst question = `'${stage}' is a protected stage. Run ${commandArgs.join(' ')} against it?`;\n\t\tif (!(await deps.confirm(question))) throw new ExecNotConfirmed(stage);\n\t}\n\n\t// Only what reaches a service on the stack needs the server: a site's or\n\t// a mobile app's values are public addresses, and need no SSH at all.\n\tconst addresses = stackAddresses(env, stack);\n\tif (addresses.length === 0) {\n\t\tlogger.log(`📦 App: ${appName} on '${stage}'`);\n\t\tlogger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);\n\t\treturn run(commandArgs, env, options.cwd);\n\t}\n\n\tconst server = stageServer(loaded, options);\n\tlogger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);\n\n\t// The deploy's own check: SSH logs in and Docker answers, before any\n\t// tunnel is asked for — so a key the server refuses is said as that.\n\tawait deps.server.check(server);\n\n\tconst tunnels: Tunnel[] = [];\n\tconst ports = new Map<string, number>();\n\ttry {\n\t\tfor (const address of addresses) {\n\t\t\tconst published = await deps.docker.published(\n\t\t\t\t{ host: dockerHost(server) },\n\t\t\t\tstack.project,\n\t\t\t\taddress.service,\n\t\t\t\taddress.port,\n\t\t\t);\n\t\t\tif (published === undefined) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`⚠️ ${address.service} is not published on the server (no running ` +\n\t\t\t\t\t\t`${stack.project} ${address.service} publishes ${address.port}), so ` +\n\t\t\t\t\t\t'it is unreachable from here; its address is passed on unchanged.',\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst tunnel = await deps.server.tunnel(server, published);\n\t\t\ttunnels.push(tunnel);\n\t\t\tports.set(`${address.service}:${address.port}`, tunnel.port);\n\t\t\tlogger.log(`🚇 ${address.service} → 127.0.0.1:${tunnel.port} (over SSH)`);\n\t\t}\n\n\t\tconst credentials = throughTunnels(env, stack, ports);\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length} value(s) for '${stage}'`,\n\t\t);\n\t\treturn await run(commandArgs, credentials, options.cwd);\n\t} finally {\n\t\tawait Promise.all(tunnels.map((tunnel) => tunnel.close()));\n\t}\n}\n\n/**\n * The command, with the values in its environment and its Credentials. A\n * Ctrl-C reaches it — it is in the terminal's process group — and this\n * process waits for it to exit rather than leaving before the tunnels close.\n */\nfunction run(\n\tcommandArgs: readonly [string, ...string[]],\n\tcredentials: Record<string, string>,\n\tcwd: string | undefined,\n): Promise<number> {\n\tconst [cmd, ...args] = commandArgs;\n\tlogger.log(`🚀 Running: ${commandArgs.join(' ')}`);\n\n\tconst inherited = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst nodeOptions = [inherited, credentialsImport(Object.keys(credentials))]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst wait = () => {};\n\tprocess.on('SIGINT', wait);\n\tprocess.on('SIGTERM', wait);\n\treturn new Promise<number>((resolve) => {\n\t\tconst child = spawn(cmd, args, {\n\t\t\t...(cwd ? { cwd } : {}),\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, ...credentials, NODE_OPTIONS: nodeOptions },\n\t\t});\n\t\tchild.on('close', (code, signal) => {\n\t\t\tresolve(code ?? 128 + (signal ? constants.signals[signal] : 0));\n\t\t});\n\t\tchild.on('error', (error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t}).finally(() => {\n\t\tprocess.off('SIGINT', wait);\n\t\tprocess.off('SIGTERM', wait);\n\t});\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
2
2
|
const require_errors = require("./errors-DMC4Umsb.cjs");
|
|
3
|
-
const require_compose = require("./compose-
|
|
3
|
+
const require_compose = require("./compose-bKLAtHLj.cjs");
|
|
4
4
|
const require_workspace = require("./workspace-BwqQnool.cjs");
|
|
5
|
-
const require_dns = require("./dns-
|
|
6
|
-
const require_seeds = require("./seeds-
|
|
5
|
+
const require_dns = require("./dns-CfEZHt7r.cjs");
|
|
6
|
+
const require_seeds = require("./seeds-CFG-f24B.cjs");
|
|
7
7
|
const require_identity = require("./identity-CbLfgQNT.cjs");
|
|
8
8
|
const require_discover = require("./discover-DozUKs8f.cjs");
|
|
9
9
|
const require_config = require("./config.cjs");
|
|
@@ -310,4 +310,4 @@ Object.defineProperty(exports, "stage_exports", {
|
|
|
310
310
|
}
|
|
311
311
|
});
|
|
312
312
|
|
|
313
|
-
//# sourceMappingURL=stage-
|
|
313
|
+
//# sourceMappingURL=stage-CJysBXWB.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage-DEQJGW3r.cjs","names":["GkmError","dockerCompose","sshAccess","prompt","edgeAlias","loadWorkspaceAppInfo","NotInAnApp","loadWorkspaceConfig","deploysWithCompose","discover","constructGlobs","secretsStoreFor","deployedStackSecrets","composeStack","deployIdentity","edgePorts","composeServer","dockerHost","spawn","constants"],"sources":["../src/exec/stage.ts"],"sourcesContent":["/**\n * `gkm exec --stage <stage> -- <command>`: a command run on this machine as\n * a deployed compose stage's app — a data import, a backfill, a report.\n *\n * The app's environment is the one its container runs with, derived the way\n * the deploy derives it (`composeStack`) from the stage's secrets, read from\n * the stage's store the way the deploy reads them. Only what the container\n * reaches on the stack's network is moved: each service it names that the\n * server publishes on its loopback — Postgres, MinIO — is reached through an\n * SSH tunnel on the deploy's own login (`ServerAccess.tunnel`), and each\n * sibling app through its public address.\n *\n * Nothing is written to disk. The values exist only in the command's\n * environment, and its Credentials are filled from there by a preload that\n * is a `data:` URL naming the keys — never their values.\n */\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { type ComposeDocker, dockerCompose } from '../compose/docker.js';\nimport { deployedStackSecrets } from '../compose/secrets.js';\nimport {\n\ttype ComposeServer,\n\tcomposeServer,\n\tdockerHost,\n\ttype ServerAccess,\n\tsshAccess,\n\ttype Tunnel,\n} from '../compose/server.js';\nimport { type ComposeStack, composeStack } from '../compose/stack.js';\nimport { edgeAlias } from '../compose/traefik.js';\nimport {\n\tloadWorkspaceAppInfo,\n\tloadWorkspaceConfig,\n\tNotInAnApp,\n} from '../config.js';\nimport { deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\nimport { prompt } from '../prompt.js';\nimport { deploysWithCompose } from '../providers/dns.js';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\n/** The stage is not one the compose target deploys. */\nexport class ExecStageNotComposed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`gkm exec --stage runs a command against a compose stage's server, and ` +\n\t\t\t\t`'${stage}' is not deployed with compose. Set deploy.default: 'compose' ` +\n\t\t\t\t'in gkm.config.ts, or run the command where the stage runs.',\n\t\t);\n\t\tthis.name = 'ExecStageNotComposed';\n\t}\n}\n\n/** The stage's secrets lack what its deploy generates: it was never deployed. */\nexport class ExecStageNotDeployed extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly missing: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${missing.join(', ')} in its secrets yet, so what its ` +\n\t\t\t\t'apps run with is not decided. Deploy it first: ' +\n\t\t\t\t`gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'ExecStageNotDeployed';\n\t}\n}\n\n/** The directory is not one of the stack's apps. */\nexport class ExecAppNotInStack extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly app: string | undefined,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${app ? `'${app}' is not` : 'This directory is not in'} one of the apps ` +\n\t\t\t\t`'${stage}' runs (${apps.join(', ')}), so there is no environment to run ` +\n\t\t\t\t`as. Run from an app's directory: cd apps/${apps[0] ?? '<app>'} && ` +\n\t\t\t\t`gkm exec --stage ${stage} -- <command>.`,\n\t\t);\n\t\tthis.name = 'ExecAppNotInStack';\n\t}\n}\n\n/** A protected stage, and nobody said yes. */\nexport class ExecNotConfirmed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(`Nothing was run against '${stage}'.`);\n\t\tthis.name = 'ExecNotConfirmed';\n\t}\n}\n\nexport interface StageExecOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\t/**\n\t * The SSH login to reach the server as, in place of\n\t * `deploy.compose.server.<stage>.user` — a person's own, where the\n\t * deploy's key lives only in CI.\n\t */\n\tsshUser?: string;\n\t/** Run against a protected stage without asking. */\n\tyes?: boolean;\n\tcwd?: string;\n}\n\n/** What the command needs from outside the process, so tests can stand in. */\nexport interface StageExecDeps {\n\t/** The server's Docker, asked which port each service publishes. */\n\tdocker: ComposeDocker;\n\t/** The tunnels to the server's loopback. */\n\tserver: ServerAccess;\n\t/** Asked before running against a protected stage. */\n\tconfirm: (question: string) => Promise<boolean>;\n\t/** Where the edge's ports are read from, as the deploy reads them. */\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport const defaultStageExecDeps: StageExecDeps = {\n\tdocker: dockerCompose,\n\tserver: sshAccess,\n\tconfirm: async (question) => {\n\t\tconst answer = await prompt(`${question} [y/N] `, {\n\t\t\tinstead: 'Pass --yes to run without asking.',\n\t\t});\n\t\treturn /^y(es)?$/i.test(answer.trim());\n\t},\n\tenv: process.env,\n};\n\n/** A service on the stack's network the app's environment names. */\nexport interface StackAddress {\n\tservice: string;\n\tport: number;\n}\n\n/**\n * The environment an app runs with on the stage: a backend's container's,\n * over the keys of each worker built from it; a site's or a mobile app's,\n * the public values its build reads.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: Pick<ComposeStack, 'stage' | 'apps' | 'workers' | 'mobileApps'>,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst mobile = stack.mobileApps.find(\n\t\t(candidate) => candidate.name === appName,\n\t);\n\tif (mobile) return { ...mobile.values };\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(stack.stage, appName, [\n\t\t\t...stack.apps.map((candidate) => candidate.name),\n\t\t\t...stack.mobileApps.map((candidate) => candidate.name),\n\t\t]);\n\t}\n\tif (app.kind === 'site') return { ...app.build?.args };\n\tconst workers = stack.workers.filter((worker) => worker.host === app.name);\n\treturn Object.assign(\n\t\t{},\n\t\t...workers.map((worker) => worker.env ?? {}),\n\t\tapp.env ?? {},\n\t);\n}\n\n/** `host:port`, at a URL's authority — after `//` or a login's `@`. */\nfunction authority(host: string, port: number): RegExp {\n\treturn new RegExp(\n\t\t`(?<=[/@,])${host.replace(/[.-]/g, '\\\\$&')}:${port}(?!\\\\d)`,\n\t\t'g',\n\t);\n}\n\n/**\n * Each of the stack's services the environment reaches on the stack's\n * network, once: `postgres:5432`, `minio:9000`.\n */\nexport function stackAddresses(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'infra'>,\n): StackAddress[] {\n\tconst found = new Map<string, StackAddress>();\n\tconst pattern = /(?<=[/@,])([a-z0-9-]+):(\\d+)(?!\\d)/g;\n\tfor (const value of Object.values(env)) {\n\t\tfor (const match of value.matchAll(pattern)) {\n\t\t\tconst service = match[1]!;\n\t\t\tif (!stack.infra.includes(service)) continue;\n\t\t\tconst port = Number(match[2]);\n\t\t\tfound.set(`${service}:${port}`, { service, port });\n\t\t}\n\t}\n\treturn [...found.values()];\n}\n\n/**\n * The environment as this machine reaches it: each service at its tunnel's\n * local port, and each sibling app at its public address. What has no\n * tunnel is left as the container has it.\n */\nexport function throughTunnels(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'apps' | 'project'>,\n\ttunnels: ReadonlyMap<string, number>,\n): Record<string, string> {\n\tconst rewrites: [RegExp, string][] = [];\n\tfor (const [address, port] of tunnels) {\n\t\tconst [service, inside] = address.split(':');\n\t\trewrites.push([authority(service!, Number(inside)), `127.0.0.1:${port}`]);\n\t}\n\tfor (const app of stack.apps) {\n\t\tfor (const host of [app.name, edgeAlias(stack.project, app.name)]) {\n\t\t\trewrites.push([\n\t\t\t\tnew RegExp(\n\t\t\t\t\t`http://${host.replace(/[.-]/g, '\\\\$&')}:${app.port}(?!\\\\d)`,\n\t\t\t\t\t'g',\n\t\t\t\t),\n\t\t\t\tapp.url,\n\t\t\t]);\n\t\t}\n\t}\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tout[key] = rewrites.reduce(\n\t\t\t(current, [pattern, replacement]) =>\n\t\t\t\tcurrent.replace(pattern, () => replacement),\n\t\t\tvalue,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * The preload that fills Credentials from the command's own environment: a\n * `data:` URL holding the keys, so no file — and no value — is written.\n */\nexport function credentialsImport(keys: readonly string[]): string {\n\tconst source =\n\t\t'globalThis.__gkm_credentials__=Object.fromEntries(' +\n\t\t`${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;\n\treturn `--import=data:text/javascript,${encodeURIComponent(source)}`;\n}\n\n/** A deployed compose stage, as its deploy has it, and the app here. */\nexport interface LoadedStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tstack: ComposeStack;\n\t/** The stage's secrets, as its deploy reads them. */\n\tsecrets: StageSecrets;\n\t/** The app this directory is, if it is one. */\n\tappName: string | undefined;\n\tprotectedStage: boolean;\n}\n\n/**\n * The stage's stack, and the app this directory is, as the deploy has them:\n * its secrets read from its store, its values derived as the deploy derives\n * them. Nothing is reached but the store — no server, no SSH.\n *\n * @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}\n */\nexport async function loadStage(\n\toptions: Pick<StageExecOptions, 'stage' | 'profile' | 'cwd'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<LoadedStage> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst { stage } = options;\n\t// Outside an app the workspace is still loaded, so the stage is checked\n\t// before the directory is refused.\n\tconst info = await loadWorkspaceAppInfo(cwd).catch((error) => {\n\t\tif (error instanceof NotInAnApp) return undefined;\n\t\tthrow error;\n\t});\n\tconst workspace =\n\t\tinfo?.workspace ?? (await loadWorkspaceConfig(cwd)).workspace;\n\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!deploysWithCompose(workspace)) throw new ExecStageNotComposed(stage);\n\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest: ConstructManifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\n\tconst store = await secretsStoreFor(workspace, stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { secrets, generated } = deployedStackSecrets(\n\t\tworkspace,\n\t\tstage,\n\t\tawait store.read(stage),\n\t\tmanifest,\n\t);\n\tif (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);\n\n\tconst stack = composeStack({\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\tstage,\n\t\tidentity: deployIdentity(workspace, stage),\n\t\t// Built, so a site's public values are resolved as its build has them.\n\t\timages: { mode: 'build', tag: 'exec' },\n\t\tsecrets,\n\t\tports: edgePorts(env),\n\t});\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tstack,\n\t\tsecrets,\n\t\tappName: info?.appName,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\n}\n\n/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */\nfunction stageServer(\n\tloaded: LoadedStage,\n\toptions: StageExecOptions,\n): ComposeServer {\n\tconst server = composeServer({\n\t\tstage: options.stage,\n\t\tlocal: false,\n\t\tconfig: loaded.workspace.deploy?.compose?.server,\n\t\tcustom: loaded.secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn server;\n}\n\n/**\n * Run `commandArgs` as the app this directory is, on the stage — through a\n * tunnel to each service it reaches — and return its exit code. Every tunnel\n * is closed once it exits, whatever it exits with.\n *\n * @throws {ExecStageNotComposed}, {ExecStageNotDeployed}, {ExecAppNotInStack},\n * {ExecNotConfirmed}, {ComposeServerUnreachable}\n */\nexport async function execOnStage(\n\tcommandArgs: readonly [string, ...string[]],\n\toptions: StageExecOptions,\n\tdeps: StageExecDeps = defaultStageExecDeps,\n): Promise<number> {\n\tconst { stage } = options;\n\tconst loaded = await loadStage(options, deps.env);\n\tconst { stack, appName, protectedStage } = loaded;\n\tconst env = appEnvironment(stack, appName);\n\n\tif (protectedStage && !options.yes) {\n\t\tconst question = `'${stage}' is a protected stage. Run ${commandArgs.join(' ')} against it?`;\n\t\tif (!(await deps.confirm(question))) throw new ExecNotConfirmed(stage);\n\t}\n\n\t// Only what reaches a service on the stack needs the server: a site's or\n\t// a mobile app's values are public addresses, and need no SSH at all.\n\tconst addresses = stackAddresses(env, stack);\n\tif (addresses.length === 0) {\n\t\tlogger.log(`📦 App: ${appName} on '${stage}'`);\n\t\tlogger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);\n\t\treturn run(commandArgs, env, options.cwd);\n\t}\n\n\tconst server = stageServer(loaded, options);\n\tlogger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);\n\n\t// The deploy's own check: SSH logs in and Docker answers, before any\n\t// tunnel is asked for — so a key the server refuses is said as that.\n\tawait deps.server.check(server);\n\n\tconst tunnels: Tunnel[] = [];\n\tconst ports = new Map<string, number>();\n\ttry {\n\t\tfor (const address of addresses) {\n\t\t\tconst published = await deps.docker.published(\n\t\t\t\t{ host: dockerHost(server) },\n\t\t\t\tstack.project,\n\t\t\t\taddress.service,\n\t\t\t\taddress.port,\n\t\t\t);\n\t\t\tif (published === undefined) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`⚠️ ${address.service} is not published on the server (no running ` +\n\t\t\t\t\t\t`${stack.project} ${address.service} publishes ${address.port}), so ` +\n\t\t\t\t\t\t'it is unreachable from here; its address is passed on unchanged.',\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst tunnel = await deps.server.tunnel(server, published);\n\t\t\ttunnels.push(tunnel);\n\t\t\tports.set(`${address.service}:${address.port}`, tunnel.port);\n\t\t\tlogger.log(`🚇 ${address.service} → 127.0.0.1:${tunnel.port} (over SSH)`);\n\t\t}\n\n\t\tconst credentials = throughTunnels(env, stack, ports);\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length} value(s) for '${stage}'`,\n\t\t);\n\t\treturn await run(commandArgs, credentials, options.cwd);\n\t} finally {\n\t\tawait Promise.all(tunnels.map((tunnel) => tunnel.close()));\n\t}\n}\n\n/**\n * The command, with the values in its environment and its Credentials. A\n * Ctrl-C reaches it — it is in the terminal's process group — and this\n * process waits for it to exit rather than leaving before the tunnels close.\n */\nfunction run(\n\tcommandArgs: readonly [string, ...string[]],\n\tcredentials: Record<string, string>,\n\tcwd: string | undefined,\n): Promise<number> {\n\tconst [cmd, ...args] = commandArgs;\n\tlogger.log(`🚀 Running: ${commandArgs.join(' ')}`);\n\n\tconst inherited = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst nodeOptions = [inherited, credentialsImport(Object.keys(credentials))]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst wait = () => {};\n\tprocess.on('SIGINT', wait);\n\tprocess.on('SIGTERM', wait);\n\treturn new Promise<number>((resolve) => {\n\t\tconst child = spawn(cmd, args, {\n\t\t\t...(cwd ? { cwd } : {}),\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, ...credentials, NODE_OPTIONS: nodeOptions },\n\t\t});\n\t\tchild.on('close', (code, signal) => {\n\t\t\tresolve(code ?? 128 + (signal ? constants.signals[signal] : 0));\n\t\t});\n\t\tchild.on('error', (error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t}).finally(() => {\n\t\tprocess.off('SIGINT', wait);\n\t\tprocess.off('SIGTERM', wait);\n\t});\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stage-CJysBXWB.cjs","names":["GkmError","dockerCompose","sshAccess","prompt","edgeAlias","loadWorkspaceAppInfo","NotInAnApp","loadWorkspaceConfig","deploysWithCompose","discover","constructGlobs","secretsStoreFor","deployedStackSecrets","composeStack","deployIdentity","edgePorts","composeServer","dockerHost","spawn","constants"],"sources":["../src/exec/stage.ts"],"sourcesContent":["/**\n * `gkm exec --stage <stage> -- <command>`: a command run on this machine as\n * a deployed compose stage's app — a data import, a backfill, a report.\n *\n * The app's environment is the one its container runs with, derived the way\n * the deploy derives it (`composeStack`) from the stage's secrets, read from\n * the stage's store the way the deploy reads them. Only what the container\n * reaches on the stack's network is moved: each service it names that the\n * server publishes on its loopback — Postgres, MinIO — is reached through an\n * SSH tunnel on the deploy's own login (`ServerAccess.tunnel`), and each\n * sibling app through its public address.\n *\n * Nothing is written to disk. The values exist only in the command's\n * environment, and its Credentials are filled from there by a preload that\n * is a `data:` URL naming the keys — never their values.\n */\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { type ComposeDocker, dockerCompose } from '../compose/docker.js';\nimport { deployedStackSecrets } from '../compose/secrets.js';\nimport {\n\ttype ComposeServer,\n\tcomposeServer,\n\tdockerHost,\n\ttype ServerAccess,\n\tsshAccess,\n\ttype Tunnel,\n} from '../compose/server.js';\nimport { type ComposeStack, composeStack } from '../compose/stack.js';\nimport { edgeAlias } from '../compose/traefik.js';\nimport {\n\tloadWorkspaceAppInfo,\n\tloadWorkspaceConfig,\n\tNotInAnApp,\n} from '../config.js';\nimport { deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\nimport { prompt } from '../prompt.js';\nimport { deploysWithCompose } from '../providers/dns.js';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\n/** The stage is not one the compose target deploys. */\nexport class ExecStageNotComposed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`gkm exec --stage runs a command against a compose stage's server, and ` +\n\t\t\t\t`'${stage}' is not deployed with compose. Set deploy.default: 'compose' ` +\n\t\t\t\t'in gkm.config.ts, or run the command where the stage runs.',\n\t\t);\n\t\tthis.name = 'ExecStageNotComposed';\n\t}\n}\n\n/** The stage's secrets lack what its deploy generates: it was never deployed. */\nexport class ExecStageNotDeployed extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly missing: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${missing.join(', ')} in its secrets yet, so what its ` +\n\t\t\t\t'apps run with is not decided. Deploy it first: ' +\n\t\t\t\t`gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'ExecStageNotDeployed';\n\t}\n}\n\n/** The directory is not one of the stack's apps. */\nexport class ExecAppNotInStack extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly app: string | undefined,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${app ? `'${app}' is not` : 'This directory is not in'} one of the apps ` +\n\t\t\t\t`'${stage}' runs (${apps.join(', ')}), so there is no environment to run ` +\n\t\t\t\t`as. Run from an app's directory: cd apps/${apps[0] ?? '<app>'} && ` +\n\t\t\t\t`gkm exec --stage ${stage} -- <command>.`,\n\t\t);\n\t\tthis.name = 'ExecAppNotInStack';\n\t}\n}\n\n/** A protected stage, and nobody said yes. */\nexport class ExecNotConfirmed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(`Nothing was run against '${stage}'.`);\n\t\tthis.name = 'ExecNotConfirmed';\n\t}\n}\n\nexport interface StageExecOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\t/**\n\t * The SSH login to reach the server as, in place of\n\t * `deploy.compose.server.<stage>.user` — a person's own, where the\n\t * deploy's key lives only in CI.\n\t */\n\tsshUser?: string;\n\t/** Run against a protected stage without asking. */\n\tyes?: boolean;\n\tcwd?: string;\n}\n\n/** What the command needs from outside the process, so tests can stand in. */\nexport interface StageExecDeps {\n\t/** The server's Docker, asked which port each service publishes. */\n\tdocker: ComposeDocker;\n\t/** The tunnels to the server's loopback. */\n\tserver: ServerAccess;\n\t/** Asked before running against a protected stage. */\n\tconfirm: (question: string) => Promise<boolean>;\n\t/** Where the edge's ports are read from, as the deploy reads them. */\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport const defaultStageExecDeps: StageExecDeps = {\n\tdocker: dockerCompose,\n\tserver: sshAccess,\n\tconfirm: async (question) => {\n\t\tconst answer = await prompt(`${question} [y/N] `, {\n\t\t\tinstead: 'Pass --yes to run without asking.',\n\t\t});\n\t\treturn /^y(es)?$/i.test(answer.trim());\n\t},\n\tenv: process.env,\n};\n\n/** A service on the stack's network the app's environment names. */\nexport interface StackAddress {\n\tservice: string;\n\tport: number;\n}\n\n/**\n * The environment an app runs with on the stage: a backend's container's,\n * over the keys of each worker built from it; a site's or a mobile app's,\n * the public values its build reads.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: Pick<ComposeStack, 'stage' | 'apps' | 'workers' | 'mobileApps'>,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst mobile = stack.mobileApps.find(\n\t\t(candidate) => candidate.name === appName,\n\t);\n\tif (mobile) return { ...mobile.values };\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(stack.stage, appName, [\n\t\t\t...stack.apps.map((candidate) => candidate.name),\n\t\t\t...stack.mobileApps.map((candidate) => candidate.name),\n\t\t]);\n\t}\n\tif (app.kind === 'site') return { ...app.build?.args };\n\tconst workers = stack.workers.filter((worker) => worker.host === app.name);\n\treturn Object.assign(\n\t\t{},\n\t\t...workers.map((worker) => worker.env ?? {}),\n\t\tapp.env ?? {},\n\t);\n}\n\n/** `host:port`, at a URL's authority — after `//` or a login's `@`. */\nfunction authority(host: string, port: number): RegExp {\n\treturn new RegExp(\n\t\t`(?<=[/@,])${host.replace(/[.-]/g, '\\\\$&')}:${port}(?!\\\\d)`,\n\t\t'g',\n\t);\n}\n\n/**\n * Each of the stack's services the environment reaches on the stack's\n * network, once: `postgres:5432`, `minio:9000`.\n */\nexport function stackAddresses(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'infra'>,\n): StackAddress[] {\n\tconst found = new Map<string, StackAddress>();\n\tconst pattern = /(?<=[/@,])([a-z0-9-]+):(\\d+)(?!\\d)/g;\n\tfor (const value of Object.values(env)) {\n\t\tfor (const match of value.matchAll(pattern)) {\n\t\t\tconst service = match[1]!;\n\t\t\tif (!stack.infra.includes(service)) continue;\n\t\t\tconst port = Number(match[2]);\n\t\t\tfound.set(`${service}:${port}`, { service, port });\n\t\t}\n\t}\n\treturn [...found.values()];\n}\n\n/**\n * The environment as this machine reaches it: each service at its tunnel's\n * local port, and each sibling app at its public address. What has no\n * tunnel is left as the container has it.\n */\nexport function throughTunnels(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'apps' | 'project'>,\n\ttunnels: ReadonlyMap<string, number>,\n): Record<string, string> {\n\tconst rewrites: [RegExp, string][] = [];\n\tfor (const [address, port] of tunnels) {\n\t\tconst [service, inside] = address.split(':');\n\t\trewrites.push([authority(service!, Number(inside)), `127.0.0.1:${port}`]);\n\t}\n\tfor (const app of stack.apps) {\n\t\tfor (const host of [app.name, edgeAlias(stack.project, app.name)]) {\n\t\t\trewrites.push([\n\t\t\t\tnew RegExp(\n\t\t\t\t\t`http://${host.replace(/[.-]/g, '\\\\$&')}:${app.port}(?!\\\\d)`,\n\t\t\t\t\t'g',\n\t\t\t\t),\n\t\t\t\tapp.url,\n\t\t\t]);\n\t\t}\n\t}\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tout[key] = rewrites.reduce(\n\t\t\t(current, [pattern, replacement]) =>\n\t\t\t\tcurrent.replace(pattern, () => replacement),\n\t\t\tvalue,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * The preload that fills Credentials from the command's own environment: a\n * `data:` URL holding the keys, so no file — and no value — is written.\n */\nexport function credentialsImport(keys: readonly string[]): string {\n\tconst source =\n\t\t'globalThis.__gkm_credentials__=Object.fromEntries(' +\n\t\t`${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;\n\treturn `--import=data:text/javascript,${encodeURIComponent(source)}`;\n}\n\n/** A deployed compose stage, as its deploy has it, and the app here. */\nexport interface LoadedStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tstack: ComposeStack;\n\t/** The stage's secrets, as its deploy reads them. */\n\tsecrets: StageSecrets;\n\t/** The app this directory is, if it is one. */\n\tappName: string | undefined;\n\tprotectedStage: boolean;\n}\n\n/**\n * The stage's stack, and the app this directory is, as the deploy has them:\n * its secrets read from its store, its values derived as the deploy derives\n * them. Nothing is reached but the store — no server, no SSH.\n *\n * @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}\n */\nexport async function loadStage(\n\toptions: Pick<StageExecOptions, 'stage' | 'profile' | 'cwd'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<LoadedStage> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst { stage } = options;\n\t// Outside an app the workspace is still loaded, so the stage is checked\n\t// before the directory is refused.\n\tconst info = await loadWorkspaceAppInfo(cwd).catch((error) => {\n\t\tif (error instanceof NotInAnApp) return undefined;\n\t\tthrow error;\n\t});\n\tconst workspace =\n\t\tinfo?.workspace ?? (await loadWorkspaceConfig(cwd)).workspace;\n\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!deploysWithCompose(workspace)) throw new ExecStageNotComposed(stage);\n\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest: ConstructManifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\n\tconst store = await secretsStoreFor(workspace, stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { secrets, generated } = deployedStackSecrets(\n\t\tworkspace,\n\t\tstage,\n\t\tawait store.read(stage),\n\t\tmanifest,\n\t);\n\tif (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);\n\n\tconst stack = composeStack({\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\tstage,\n\t\tidentity: deployIdentity(workspace, stage),\n\t\t// Built, so a site's public values are resolved as its build has them.\n\t\timages: { mode: 'build', tag: 'exec' },\n\t\tsecrets,\n\t\tports: edgePorts(env),\n\t});\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tstack,\n\t\tsecrets,\n\t\tappName: info?.appName,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\n}\n\n/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */\nfunction stageServer(\n\tloaded: LoadedStage,\n\toptions: StageExecOptions,\n): ComposeServer {\n\tconst server = composeServer({\n\t\tstage: options.stage,\n\t\tlocal: false,\n\t\tconfig: loaded.workspace.deploy?.compose?.server,\n\t\tcustom: loaded.secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn server;\n}\n\n/**\n * Run `commandArgs` as the app this directory is, on the stage — through a\n * tunnel to each service it reaches — and return its exit code. Every tunnel\n * is closed once it exits, whatever it exits with.\n *\n * @throws {ExecStageNotComposed}, {ExecStageNotDeployed}, {ExecAppNotInStack},\n * {ExecNotConfirmed}, {ComposeServerUnreachable}\n */\nexport async function execOnStage(\n\tcommandArgs: readonly [string, ...string[]],\n\toptions: StageExecOptions,\n\tdeps: StageExecDeps = defaultStageExecDeps,\n): Promise<number> {\n\tconst { stage } = options;\n\tconst loaded = await loadStage(options, deps.env);\n\tconst { stack, appName, protectedStage } = loaded;\n\tconst env = appEnvironment(stack, appName);\n\n\tif (protectedStage && !options.yes) {\n\t\tconst question = `'${stage}' is a protected stage. Run ${commandArgs.join(' ')} against it?`;\n\t\tif (!(await deps.confirm(question))) throw new ExecNotConfirmed(stage);\n\t}\n\n\t// Only what reaches a service on the stack needs the server: a site's or\n\t// a mobile app's values are public addresses, and need no SSH at all.\n\tconst addresses = stackAddresses(env, stack);\n\tif (addresses.length === 0) {\n\t\tlogger.log(`📦 App: ${appName} on '${stage}'`);\n\t\tlogger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);\n\t\treturn run(commandArgs, env, options.cwd);\n\t}\n\n\tconst server = stageServer(loaded, options);\n\tlogger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);\n\n\t// The deploy's own check: SSH logs in and Docker answers, before any\n\t// tunnel is asked for — so a key the server refuses is said as that.\n\tawait deps.server.check(server);\n\n\tconst tunnels: Tunnel[] = [];\n\tconst ports = new Map<string, number>();\n\ttry {\n\t\tfor (const address of addresses) {\n\t\t\tconst published = await deps.docker.published(\n\t\t\t\t{ host: dockerHost(server) },\n\t\t\t\tstack.project,\n\t\t\t\taddress.service,\n\t\t\t\taddress.port,\n\t\t\t);\n\t\t\tif (published === undefined) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`⚠️ ${address.service} is not published on the server (no running ` +\n\t\t\t\t\t\t`${stack.project} ${address.service} publishes ${address.port}), so ` +\n\t\t\t\t\t\t'it is unreachable from here; its address is passed on unchanged.',\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst tunnel = await deps.server.tunnel(server, published);\n\t\t\ttunnels.push(tunnel);\n\t\t\tports.set(`${address.service}:${address.port}`, tunnel.port);\n\t\t\tlogger.log(`🚇 ${address.service} → 127.0.0.1:${tunnel.port} (over SSH)`);\n\t\t}\n\n\t\tconst credentials = throughTunnels(env, stack, ports);\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length} value(s) for '${stage}'`,\n\t\t);\n\t\treturn await run(commandArgs, credentials, options.cwd);\n\t} finally {\n\t\tawait Promise.all(tunnels.map((tunnel) => tunnel.close()));\n\t}\n}\n\n/**\n * The command, with the values in its environment and its Credentials. A\n * Ctrl-C reaches it — it is in the terminal's process group — and this\n * process waits for it to exit rather than leaving before the tunnels close.\n */\nfunction run(\n\tcommandArgs: readonly [string, ...string[]],\n\tcredentials: Record<string, string>,\n\tcwd: string | undefined,\n): Promise<number> {\n\tconst [cmd, ...args] = commandArgs;\n\tlogger.log(`🚀 Running: ${commandArgs.join(' ')}`);\n\n\tconst inherited = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst nodeOptions = [inherited, credentialsImport(Object.keys(credentials))]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst wait = () => {};\n\tprocess.on('SIGINT', wait);\n\tprocess.on('SIGTERM', wait);\n\treturn new Promise<number>((resolve) => {\n\t\tconst child = spawn(cmd, args, {\n\t\t\t...(cwd ? { cwd } : {}),\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, ...credentials, NODE_OPTIONS: nodeOptions },\n\t\t});\n\t\tchild.on('close', (code, signal) => {\n\t\t\tresolve(code ?? 128 + (signal ? constants.signals[signal] : 0));\n\t\t});\n\t\tchild.on('error', (error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t}).finally(() => {\n\t\tprocess.off('SIGINT', wait);\n\t\tprocess.off('SIGTERM', wait);\n\t});\n}\n"],"mappings":"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"}
|
package/dist/target/public.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_define = require("../define-crmMcXFS.cjs");
|
|
3
|
-
const require_resolve = require("../resolve-
|
|
3
|
+
const require_resolve = require("../resolve-CyprRcQo.cjs");
|
|
4
4
|
const require_provider = require("../provider-BqpjG9QM.cjs");
|
|
5
5
|
exports.DeployTargetNotYetSupported = require_define.DeployTargetNotYetSupported;
|
|
6
6
|
exports.InvalidTargetOptions = require_resolve.InvalidTargetOptions;
|
package/dist/target/public.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { a as UnknownDeployTarget, i as DeployTargetNotYetSupported, n as isDeployTarget, t as defineTarget } from "../define-6poNqm5O.mjs";
|
|
2
|
-
import { i as TargetRuntimeMismatch, l as TargetPackageNotFound, n as TargetEntryInvalid, r as TargetPackageInvalid, s as TargetRuntimeUndeclared, t as InvalidTargetOptions } from "../resolve-
|
|
2
|
+
import { i as TargetRuntimeMismatch, l as TargetPackageNotFound, n as TargetEntryInvalid, r as TargetPackageInvalid, s as TargetRuntimeUndeclared, t as InvalidTargetOptions } from "../resolve-DSilYpzm.mjs";
|
|
3
3
|
import { t as ProviderRemoved } from "../provider-CEOKk8NS.mjs";
|
|
4
4
|
export { DeployTargetNotYetSupported, InvalidTargetOptions, ProviderRemoved, TargetEntryInvalid, TargetPackageInvalid, TargetPackageNotFound, TargetRuntimeMismatch, TargetRuntimeUndeclared, UnknownDeployTarget, defineTarget, isDeployTarget };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geekmidas/cli",
|
|
3
|
-
"version": "10.0.0-alpha.
|
|
3
|
+
"version": "10.0.0-alpha.122",
|
|
4
4
|
"description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -120,16 +120,16 @@
|
|
|
120
120
|
"prompts": "~2.4.2",
|
|
121
121
|
"tsx": "~4.23.15",
|
|
122
122
|
"yaml": "~2.9.1",
|
|
123
|
-
"@geekmidas/cache": "10.0.0-alpha.
|
|
124
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
125
|
-
"@geekmidas/db": "10.0.0-alpha.
|
|
126
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
127
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
128
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
129
|
-
"@geekmidas/manifest": "10.0.0-alpha.
|
|
130
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
131
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
132
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
123
|
+
"@geekmidas/cache": "10.0.0-alpha.122",
|
|
124
|
+
"@geekmidas/constructs": "~10.0.0-alpha.122",
|
|
125
|
+
"@geekmidas/db": "10.0.0-alpha.122",
|
|
126
|
+
"@geekmidas/envkit": "~10.0.0-alpha.122",
|
|
127
|
+
"@geekmidas/errors": "~10.0.0-alpha.122",
|
|
128
|
+
"@geekmidas/logger": "~10.0.0-alpha.122",
|
|
129
|
+
"@geekmidas/manifest": "10.0.0-alpha.122",
|
|
130
|
+
"@geekmidas/schema": "~10.0.0-alpha.122",
|
|
131
|
+
"@geekmidas/services": "~10.0.0-alpha.122",
|
|
132
|
+
"@geekmidas/storage": "~10.0.0-alpha.122"
|
|
133
133
|
},
|
|
134
134
|
"devDependencies": {
|
|
135
135
|
"@hono/node-server": "~1.19.5",
|
|
@@ -144,12 +144,12 @@
|
|
|
144
144
|
"typescript": "~7.0.2",
|
|
145
145
|
"vitest": "~5.0.2",
|
|
146
146
|
"zod": "~4.6.5",
|
|
147
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
148
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
149
|
-
"@geekmidas/testkit": "10.0.0-alpha.
|
|
147
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.122",
|
|
148
|
+
"@geekmidas/events": "~10.0.0-alpha.122",
|
|
149
|
+
"@geekmidas/testkit": "10.0.0-alpha.122"
|
|
150
150
|
},
|
|
151
151
|
"peerDependencies": {
|
|
152
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
152
|
+
"@geekmidas/telescope": "~10.0.0-alpha.122"
|
|
153
153
|
},
|
|
154
154
|
"peerDependenciesMeta": {
|
|
155
155
|
"@geekmidas/telescope": {
|