@geekmidas/cli 10.0.0-alpha.110 → 10.0.0-alpha.112
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-CjeOduia.mjs → GoDaddyProvider-CPabc3XW.mjs} +2 -2
- package/dist/{GoDaddyProvider-CjeOduia.mjs.map → GoDaddyProvider-CPabc3XW.mjs.map} +1 -1
- package/dist/{GoDaddyProvider-qNUzTXI7.cjs → GoDaddyProvider-DWrDIXzQ.cjs} +2 -2
- package/dist/{GoDaddyProvider-qNUzTXI7.cjs.map → GoDaddyProvider-DWrDIXzQ.cjs.map} +1 -1
- package/dist/{LegacyStateStore-pgscYsiG.mjs → LegacyStateStore-BdXoAlYY.mjs} +3 -3
- package/dist/{LegacyStateStore-pgscYsiG.mjs.map → LegacyStateStore-BdXoAlYY.mjs.map} +1 -1
- package/dist/{LegacyStateStore-MxiSu7KJ.cjs → LegacyStateStore-fsyPGBWu.cjs} +3 -3
- package/dist/{LegacyStateStore-MxiSu7KJ.cjs.map → LegacyStateStore-fsyPGBWu.cjs.map} +1 -1
- package/dist/{LocalStateStore-Bm8Vmwjb.cjs → LocalStateStore-AtviUBky.cjs} +2 -2
- package/dist/{LocalStateStore-Bm8Vmwjb.cjs.map → LocalStateStore-AtviUBky.cjs.map} +1 -1
- package/dist/{LocalStateStore-BcvyKbAw.mjs → LocalStateStore-BrAX5yy7.mjs} +2 -2
- package/dist/{LocalStateStore-BcvyKbAw.mjs.map → LocalStateStore-BrAX5yy7.mjs.map} +1 -1
- package/dist/{S3StateStore-H2XsAzpJ.mjs → S3StateStore-Cx7njkBz.mjs} +2 -2
- package/dist/{S3StateStore-H2XsAzpJ.mjs.map → S3StateStore-Cx7njkBz.mjs.map} +1 -1
- package/dist/{S3StateStore-DXHSBVsV.cjs → S3StateStore-tn5g80Sx.cjs} +2 -2
- package/dist/{S3StateStore-DXHSBVsV.cjs.map → S3StateStore-tn5g80Sx.cjs.map} +1 -1
- package/dist/{SSMStateStore-BLPSNl-U.cjs → SSMStateStore-CAFde-Fq.cjs} +2 -2
- package/dist/{SSMStateStore-BLPSNl-U.cjs.map → SSMStateStore-CAFde-Fq.cjs.map} +1 -1
- package/dist/{SSMStateStore-CENciQEM.mjs → SSMStateStore-sqZ3oDmn.mjs} +2 -2
- package/dist/{SSMStateStore-CENciQEM.mjs.map → SSMStateStore-sqZ3oDmn.mjs.map} +1 -1
- package/dist/{add-Cdg1kEqN.cjs → add-Cs8yT5iH.cjs} +3 -3
- package/dist/{add-Cdg1kEqN.cjs.map → add-Cs8yT5iH.cjs.map} +1 -1
- package/dist/{add-snhdVmR-.mjs → add-DJE7zqJ4.mjs} +3 -3
- package/dist/{add-snhdVmR-.mjs.map → add-DJE7zqJ4.mjs.map} +1 -1
- package/dist/{bundler-Wd9H4QLJ.mjs → bundler-DrySVbXF.mjs} +5 -5
- package/dist/{bundler-Wd9H4QLJ.mjs.map → bundler-DrySVbXF.mjs.map} +1 -1
- package/dist/{bundler-B4GDxImV.cjs → bundler-NrtItVBp.cjs} +5 -5
- package/dist/{bundler-B4GDxImV.cjs.map → bundler-NrtItVBp.cjs.map} +1 -1
- package/dist/{cli-BZYz55tO.cjs → cli-CHzpFBh-.cjs} +2 -2
- package/dist/{cli-BZYz55tO.cjs.map → cli-CHzpFBh-.cjs.map} +1 -1
- package/dist/{cli-BzrDSNSg.mjs → cli-DDY3jfTt.mjs} +4 -4
- package/dist/{cli-BzrDSNSg.mjs.map → cli-DDY3jfTt.mjs.map} +1 -1
- package/dist/{compose-DN9zySvQ.cjs → compose-ByIbIrzs.cjs} +20 -5
- package/dist/compose-ByIbIrzs.cjs.map +1 -0
- package/dist/{compose-Gsd1Nq7R.mjs → compose-CDC2bCme.mjs} +20 -5
- package/dist/compose-CDC2bCme.mjs.map +1 -0
- package/dist/deploy/public.cjs +6 -6
- package/dist/deploy/public.mjs +6 -6
- package/dist/{deploy-H2S8_mTx.mjs → deploy-DLq9-m6H.mjs} +5 -5
- package/dist/{deploy-H2S8_mTx.mjs.map → deploy-DLq9-m6H.mjs.map} +1 -1
- package/dist/{deploy-CFvq8daZ.cjs → deploy-Dl_NavYE.cjs} +5 -5
- package/dist/{deploy-CFvq8daZ.cjs.map → deploy-Dl_NavYE.cjs.map} +1 -1
- package/dist/{dns-BdETzP10.cjs → dns-C2TGXUF4.cjs} +2 -2
- package/dist/{dns-BdETzP10.cjs.map → dns-C2TGXUF4.cjs.map} +1 -1
- package/dist/{dns-pFMShDqW.mjs → dns-pzftumxg.mjs} +2 -2
- package/dist/{dns-pFMShDqW.mjs.map → dns-pzftumxg.mjs.map} +1 -1
- package/dist/{dokploy-oDJbnvCb.mjs → dokploy-B7-OwY2s.mjs} +2 -2
- package/dist/{dokploy-oDJbnvCb.mjs.map → dokploy-B7-OwY2s.mjs.map} +1 -1
- package/dist/{dokploy-HioAjP7K.cjs → dokploy-OqBh8G4g.cjs} +2 -2
- package/dist/{dokploy-HioAjP7K.cjs.map → dokploy-OqBh8G4g.cjs.map} +1 -1
- package/dist/eas-Cne743nQ.cjs +337 -0
- package/dist/eas-Cne743nQ.cjs.map +1 -0
- package/dist/eas-DEpwISrQ.mjs +334 -0
- package/dist/eas-DEpwISrQ.mjs.map +1 -0
- package/dist/{encryption-D5pDJyp2.mjs → encryption-DJv_jEXi.mjs} +2 -2
- package/dist/{encryption-D5pDJyp2.mjs.map → encryption-DJv_jEXi.mjs.map} +1 -1
- package/dist/{encryption-DVCxD-gV.cjs → encryption-DoVnVU82.cjs} +2 -2
- package/dist/{encryption-DVCxD-gV.cjs.map → encryption-DoVnVU82.cjs.map} +1 -1
- package/dist/{engine-BaJGPzte.cjs → engine-CJhQb50j.cjs} +3 -3
- package/dist/{engine-BaJGPzte.cjs.map → engine-CJhQb50j.cjs.map} +1 -1
- package/dist/{engine-CfAKgr2_.mjs → engine-Db71-OZL.mjs} +3 -3
- package/dist/{engine-CfAKgr2_.mjs.map → engine-Db71-OZL.mjs.map} +1 -1
- package/dist/{github-E-ZxqFPq.mjs → github-D2eABlxM.mjs} +4 -4
- package/dist/{github-E-ZxqFPq.mjs.map → github-D2eABlxM.mjs.map} +1 -1
- package/dist/{github-D902Vwr9.cjs → github-DDOHjdS9.cjs} +4 -4
- package/dist/{github-D902Vwr9.cjs.map → github-DDOHjdS9.cjs.map} +1 -1
- package/dist/googlePlay-B6uiHi65.cjs +931 -0
- package/dist/googlePlay-B6uiHi65.cjs.map +1 -0
- package/dist/googlePlay-BYZ-J5sC.mjs +931 -0
- package/dist/googlePlay-BYZ-J5sC.mjs.map +1 -0
- package/dist/index.cjs +268 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +268 -97
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-BZ0GnSZQ.mjs → migrate-CQUGbptv.mjs} +2 -2
- package/dist/{migrate-BZ0GnSZQ.mjs.map → migrate-CQUGbptv.mjs.map} +1 -1
- package/dist/{migrate-Df81ew0k.cjs → migrate-GLBZlrlJ.cjs} +2 -2
- package/dist/{migrate-Df81ew0k.cjs.map → migrate-GLBZlrlJ.cjs.map} +1 -1
- package/dist/{migrate-CTqhWtev.cjs → migrate-bDQzFvBN.cjs} +2 -2
- package/dist/{migrate-CTqhWtev.cjs.map → migrate-bDQzFvBN.cjs.map} +1 -1
- package/dist/{migrate-DCXwHcWX.mjs → migrate-xHYaeBkd.mjs} +3 -3
- package/dist/{migrate-DCXwHcWX.mjs.map → migrate-xHYaeBkd.mjs.map} +1 -1
- package/dist/publish-CSctCNf4.mjs +123 -0
- package/dist/publish-CSctCNf4.mjs.map +1 -0
- package/dist/publish-SDG1WfYn.cjs +124 -0
- package/dist/publish-SDG1WfYn.cjs.map +1 -0
- package/dist/{resolve-BMpPd8x3.mjs → resolve-CxDqDmCD.mjs} +3 -3
- package/dist/{resolve-BMpPd8x3.mjs.map → resolve-CxDqDmCD.mjs.map} +1 -1
- package/dist/{resolve-D043YmW9.cjs → resolve-wG49Woex.cjs} +3 -3
- package/dist/{resolve-D043YmW9.cjs.map → resolve-wG49Woex.cjs.map} +1 -1
- package/dist/{rollback-CDs_XNb0.mjs → rollback-BQgEdr_p.mjs} +4 -4
- package/dist/{rollback-CDs_XNb0.mjs.map → rollback-BQgEdr_p.mjs.map} +1 -1
- package/dist/{rollback-CBiYsQ3T.cjs → rollback-Cw-Z0mWn.cjs} +4 -4
- package/dist/{rollback-CBiYsQ3T.cjs.map → rollback-Cw-Z0mWn.cjs.map} +1 -1
- package/dist/{seeds-BjcO4DDa.mjs → seeds-CIk0l4SC.mjs} +13 -10
- package/dist/seeds-CIk0l4SC.mjs.map +1 -0
- package/dist/{seeds-DNqRA1aQ.cjs → seeds-Dk8YkLkM.cjs} +18 -15
- package/dist/seeds-Dk8YkLkM.cjs.map +1 -0
- package/dist/{sniffer-DipSS8r8.cjs → sniffer-2aK7cB2u.cjs} +6 -6
- package/dist/{sniffer-DipSS8r8.cjs.map → sniffer-2aK7cB2u.cjs.map} +1 -1
- package/dist/{sniffer-b0XPmSyT.mjs → sniffer-CABMO-Bh.mjs} +6 -6
- package/dist/{sniffer-b0XPmSyT.mjs.map → sniffer-CABMO-Bh.mjs.map} +1 -1
- package/dist/{stage-DrDmZusd.mjs → stage-ClKTHQoM.mjs} +69 -33
- package/dist/stage-ClKTHQoM.mjs.map +1 -0
- package/dist/{stage-C_y2UovO.cjs → stage-DrCS0vKD.cjs} +79 -32
- package/dist/stage-DrCS0vKD.cjs.map +1 -0
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.mjs +1 -1
- package/dist/{terminal-CvRLxz6g.mjs → terminal-CybB-cjk.mjs} +2 -2
- package/dist/{terminal-CvRLxz6g.mjs.map → terminal-CybB-cjk.mjs.map} +1 -1
- package/package.json +15 -15
- package/dist/compose-DN9zySvQ.cjs.map +0 -1
- package/dist/compose-Gsd1Nq7R.mjs.map +0 -1
- package/dist/seeds-BjcO4DDa.mjs.map +0 -1
- package/dist/seeds-DNqRA1aQ.cjs.map +0 -1
- package/dist/stage-C_y2UovO.cjs.map +0 -1
- package/dist/stage-DrDmZusd.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stage-C_y2UovO.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 { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.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's container runs with: a backend's own, over the\n * keys of each worker built from it; a site's, its public values.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: ComposeStack,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(\n\t\t\tstack.stage,\n\t\t\tappName,\n\t\t\tstack.apps.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/** The stage's stack, and the app this directory is, as the deploy has them. */\nasync function stageStack(\n\toptions: StageExecOptions,\n\tenv: NodeJS.ProcessEnv,\n): Promise<{\n\tstack: ComposeStack;\n\tappName: string | undefined;\n\tserver: ComposeServer;\n\tprotectedStage: boolean;\n}> {\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\tconst server = composeServer({\n\t\tstage,\n\t\tlocal: false,\n\t\tconfig: workspace.deploy?.compose?.server,\n\t\tcustom: secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn {\n\t\tstack,\n\t\tappName: info?.appName,\n\t\tserver,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\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 { stack, appName, server, protectedStage } = await stageStack(\n\t\toptions,\n\t\tdeps.env,\n\t);\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\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 stackAddresses(env, stack)) {\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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stage-DrDmZusd.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 { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.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's container runs with: a backend's own, over the\n * keys of each worker built from it; a site's, its public values.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: ComposeStack,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(\n\t\t\tstack.stage,\n\t\t\tappName,\n\t\t\tstack.apps.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/** The stage's stack, and the app this directory is, as the deploy has them. */\nasync function stageStack(\n\toptions: StageExecOptions,\n\tenv: NodeJS.ProcessEnv,\n): Promise<{\n\tstack: ComposeStack;\n\tappName: string | undefined;\n\tserver: ComposeServer;\n\tprotectedStage: boolean;\n}> {\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\tconst server = composeServer({\n\t\tstage,\n\t\tlocal: false,\n\t\tconfig: workspace.deploy?.compose?.server,\n\t\tcustom: secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn {\n\t\tstack,\n\t\tappName: info?.appName,\n\t\tserver,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\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 { stack, appName, server, protectedStage } = await stageStack(\n\t\toptions,\n\t\tdeps.env,\n\t);\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\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 stackAddresses(env, stack)) {\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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"}
|