@geekmidas/cli 10.0.0-alpha.87 → 10.0.0-alpha.89
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--36rw8cP.mjs +387 -0
- package/dist/GoDaddyProvider--36rw8cP.mjs.map +1 -0
- package/dist/GoDaddyProvider-DnRor066.cjs +387 -0
- package/dist/GoDaddyProvider-DnRor066.cjs.map +1 -0
- package/dist/{HostingerProvider-CVc3rLPB.mjs → HostingerProvider-BI6J7oJr.mjs} +2 -2
- package/dist/{HostingerProvider-CVc3rLPB.mjs.map → HostingerProvider-BI6J7oJr.mjs.map} +1 -1
- package/dist/{HostingerProvider-BCB1QJj1.cjs → HostingerProvider-CyTpG_jy.cjs} +2 -2
- package/dist/{HostingerProvider-BCB1QJj1.cjs.map → HostingerProvider-CyTpG_jy.cjs.map} +1 -1
- package/dist/{LegacyStateStore-CWG_HVC0.cjs → LegacyStateStore-BaBoYl59.cjs} +2 -2
- package/dist/{LegacyStateStore-CWG_HVC0.cjs.map → LegacyStateStore-BaBoYl59.cjs.map} +1 -1
- package/dist/{LegacyStateStore-B0R4LlX_.mjs → LegacyStateStore-CO7bzJCU.mjs} +2 -2
- package/dist/{LegacyStateStore-B0R4LlX_.mjs.map → LegacyStateStore-CO7bzJCU.mjs.map} +1 -1
- package/dist/{LocalStateStore-CLwa2DpK.cjs → LocalStateStore-CUWh2BWU.cjs} +2 -2
- package/dist/{LocalStateStore-CLwa2DpK.cjs.map → LocalStateStore-CUWh2BWU.cjs.map} +1 -1
- package/dist/{LocalStateStore-Q4vXg2ua.mjs → LocalStateStore-C_hYDv6I.mjs} +2 -2
- package/dist/{LocalStateStore-Q4vXg2ua.mjs.map → LocalStateStore-C_hYDv6I.mjs.map} +1 -1
- package/dist/{S3StateStore-Cea9_5gF.cjs → S3StateStore-O8EYfjfG.cjs} +2 -2
- package/dist/{S3StateStore-Cea9_5gF.cjs.map → S3StateStore-O8EYfjfG.cjs.map} +1 -1
- package/dist/{S3StateStore-CksKYKRa.mjs → S3StateStore-hIm8Rrmu.mjs} +2 -2
- package/dist/{S3StateStore-CksKYKRa.mjs.map → S3StateStore-hIm8Rrmu.mjs.map} +1 -1
- package/dist/{SSMStateStore-aBOTF018.cjs → SSMStateStore-DIzbiqCz.cjs} +2 -2
- package/dist/{SSMStateStore-aBOTF018.cjs.map → SSMStateStore-DIzbiqCz.cjs.map} +1 -1
- package/dist/{SSMStateStore-BszSCAS2.mjs → SSMStateStore-IvcjTk8h.mjs} +2 -2
- package/dist/{SSMStateStore-BszSCAS2.mjs.map → SSMStateStore-IvcjTk8h.mjs.map} +1 -1
- package/dist/SubscriberGenerator-BPqQf8fB.mjs.map +1 -1
- package/dist/SubscriberGenerator-BVn7cQ-2.cjs.map +1 -1
- package/dist/{add-5MZAXybv.cjs → add-C9qX4Juc.cjs} +30 -7
- package/dist/add-C9qX4Juc.cjs.map +1 -0
- package/dist/{add-D_jNqnfZ.mjs → add-v3tCbrpE.mjs} +30 -7
- package/dist/add-v3tCbrpE.mjs.map +1 -0
- package/dist/{bundler-1REMhKps.cjs → bundler-C4NmpRTD.cjs} +5 -5
- package/dist/{bundler-1REMhKps.cjs.map → bundler-C4NmpRTD.cjs.map} +1 -1
- package/dist/{bundler-BMo-JP5s.mjs → bundler-C_fCgcVO.mjs} +5 -5
- package/dist/{bundler-BMo-JP5s.mjs.map → bundler-C_fCgcVO.mjs.map} +1 -1
- package/dist/{cli-BL8ZHsLQ.cjs → cli-C85Dut8K.cjs} +6 -5
- package/dist/cli-C85Dut8K.cjs.map +1 -0
- package/dist/{cli-7edNFwGk.mjs → cli-REdO9X5b.mjs} +7 -6
- package/dist/cli-REdO9X5b.mjs.map +1 -0
- package/dist/{compose-xw86kVYi.cjs → compose-BvrhDwJ6.cjs} +556 -30
- package/dist/compose-BvrhDwJ6.cjs.map +1 -0
- package/dist/{compose-DbJhRQlh.mjs → compose-Dn0oAgQ0.mjs} +509 -31
- package/dist/compose-Dn0oAgQ0.mjs.map +1 -0
- package/dist/{config-DrCAyoH5.d.mts → config-CdEcTrjG.d.mts} +3 -3
- package/dist/{config-CpjsVxru.d.cts.map → config-CdEcTrjG.d.mts.map} +1 -1
- package/dist/{config-CpjsVxru.d.cts → config-pST7E0CS.d.cts} +3 -3
- package/dist/{config-DrCAyoH5.d.mts.map → config-pST7E0CS.d.cts.map} +1 -1
- package/dist/config.cjs +1 -1
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +1 -1
- package/dist/{credentials-tr26-KML.cjs → credentials-BKfIugu2.cjs} +47 -2
- package/dist/credentials-BKfIugu2.cjs.map +1 -0
- package/dist/{credentials-BgJ5mS_d.mjs → credentials-CnVqoXMb.mjs} +30 -3
- package/dist/credentials-CnVqoXMb.mjs.map +1 -0
- package/dist/databases-BY-pEImp.cjs.map +1 -1
- package/dist/databases-CzszQV0N.mjs.map +1 -1
- package/dist/deploy/public.cjs +10 -10
- package/dist/deploy/public.d.cts +9 -3
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +9 -3
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +10 -10
- package/dist/{deploy-BTfmXd3g.mjs → deploy-BaERRh0Q.mjs} +10 -8
- package/dist/deploy-BaERRh0Q.mjs.map +1 -0
- package/dist/{deploy-CtbZj4zN.cjs → deploy-DbBGo3a6.cjs} +10 -8
- package/dist/deploy-DbBGo3a6.cjs.map +1 -0
- package/dist/{dokploy-r69yZaIt.mjs → dokploy-CMmD_ddX.mjs} +2 -2
- package/dist/{dokploy-r69yZaIt.mjs.map → dokploy-CMmD_ddX.mjs.map} +1 -1
- package/dist/{dokploy-TGk3oPwr.cjs → dokploy-DwAaKA9B.cjs} +2 -2
- package/dist/{dokploy-TGk3oPwr.cjs.map → dokploy-DwAaKA9B.cjs.map} +1 -1
- package/dist/{encryption-C-8Tt5Hh.mjs → encryption-BQmhq9_y.mjs} +3 -3
- package/dist/{encryption-C-8Tt5Hh.mjs.map → encryption-BQmhq9_y.mjs.map} +1 -1
- package/dist/{encryption-CDn7BH5O.cjs → encryption-BrN_9_CM.cjs} +3 -3
- package/dist/{encryption-CDn7BH5O.cjs.map → encryption-BrN_9_CM.cjs.map} +1 -1
- package/dist/{engine-jAINgiOB.mjs → engine-DILocOOH.mjs} +23 -404
- package/dist/engine-DILocOOH.mjs.map +1 -0
- package/dist/{engine-Bt5k_UMq.cjs → engine-DgHoFBMw.cjs} +25 -406
- package/dist/engine-DgHoFBMw.cjs.map +1 -0
- package/dist/{github-DFhuJQlp.cjs → github-Bf7pz6AV.cjs} +3 -3
- package/dist/{github-DFhuJQlp.cjs.map → github-Bf7pz6AV.cjs.map} +1 -1
- package/dist/{github-CZHAtwPT.mjs → github-xzg99wz8.mjs} +3 -3
- package/dist/{github-CZHAtwPT.mjs.map → github-xzg99wz8.mjs.map} +1 -1
- package/dist/{index-MnUJuUlj.d.mts → index-D-F_hTDz.d.cts} +21 -4
- package/dist/index-D-F_hTDz.d.cts.map +1 -0
- package/dist/{index-elsLJTUv.d.cts → index-YFNxR5Ns.d.mts} +21 -4
- package/dist/index-YFNxR5Ns.d.mts.map +1 -0
- package/dist/index.cjs +305 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +306 -71
- package/dist/index.mjs.map +1 -1
- package/dist/{local-Cq8qdgNX.cjs → local-BjxDfPvy.cjs} +2 -2
- package/dist/{local-Cq8qdgNX.cjs.map → local-BjxDfPvy.cjs.map} +1 -1
- package/dist/{local-Ciq-bTPr.mjs → local-IuoIF3CT.mjs} +2 -2
- package/dist/{local-Ciq-bTPr.mjs.map → local-IuoIF3CT.mjs.map} +1 -1
- package/dist/{migrate-B1rVlwj1.cjs → migrate-7xayVtzL.cjs} +3 -3
- package/dist/{migrate-B1rVlwj1.cjs.map → migrate-7xayVtzL.cjs.map} +1 -1
- package/dist/{migrate-ggkPVJRC.mjs → migrate-CKLJ73OO.mjs} +4 -4
- package/dist/{migrate-ggkPVJRC.mjs.map → migrate-CKLJ73OO.mjs.map} +1 -1
- package/dist/{migrate-Dn7nK6sh.mjs → migrate-CpmO5qnl.mjs} +3 -3
- package/dist/{migrate-Dn7nK6sh.mjs.map → migrate-CpmO5qnl.mjs.map} +1 -1
- package/dist/{migrate-B16qgYTn.cjs → migrate-DPF7ZF8M.cjs} +3 -3
- package/dist/{migrate-B16qgYTn.cjs.map → migrate-DPF7ZF8M.cjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/ready-BpNbORRB.cjs.map +1 -1
- package/dist/ready-b6vm0-W-.mjs.map +1 -1
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/{reconcile--I7V4Klj.mjs → reconcile-Dh1Jk7WE.mjs} +2 -2
- package/dist/{reconcile--I7V4Klj.mjs.map → reconcile-Dh1Jk7WE.mjs.map} +1 -1
- package/dist/{resolve-BSke45k6.mjs → resolve-Dv_weZoh.mjs} +4 -4
- package/dist/{resolve-BSke45k6.mjs.map → resolve-Dv_weZoh.mjs.map} +1 -1
- package/dist/{resolve-BXeXzpmU.cjs → resolve-dlKPFNgv.cjs} +4 -4
- package/dist/{resolve-BXeXzpmU.cjs.map → resolve-dlKPFNgv.cjs.map} +1 -1
- package/dist/{rollback-DFMiBWf0.cjs → rollback-65VVIdBM.cjs} +6 -6
- package/dist/{rollback-DFMiBWf0.cjs.map → rollback-65VVIdBM.cjs.map} +1 -1
- package/dist/{rollback-CEE6dse4.mjs → rollback-DpSU2DsD.mjs} +6 -6
- package/dist/{rollback-CEE6dse4.mjs.map → rollback-DpSU2DsD.mjs.map} +1 -1
- package/dist/{runtime-HabiNBtJ.d.mts → runtime-CN_kmqxo.d.cts} +2 -2
- package/dist/{runtime-D48ZKk8_.d.cts.map → runtime-CN_kmqxo.d.cts.map} +1 -1
- package/dist/{runtime-D48ZKk8_.d.cts → runtime-Cjvon3oj.d.mts} +2 -2
- package/dist/{runtime-HabiNBtJ.d.mts.map → runtime-Cjvon3oj.d.mts.map} +1 -1
- package/dist/{seeds-CsI0A_Ef.cjs → seeds-CN6tB-va.cjs} +646 -187
- package/dist/{seeds-CsI0A_Ef.cjs.map → seeds-CN6tB-va.cjs.map} +1 -1
- package/dist/{seeds-BYdrtZsC.mjs → seeds-perzdN81.mjs} +599 -164
- package/dist/seeds-perzdN81.mjs.map +1 -0
- package/dist/{sniffer-B0EU73bj.mjs → sniffer-BTtX3NUM.mjs} +3 -3
- package/dist/{sniffer-B0EU73bj.mjs.map → sniffer-BTtX3NUM.mjs.map} +1 -1
- package/dist/{sniffer-DJYupBeK.cjs → sniffer-IAQEpsQb.cjs} +3 -3
- package/dist/{sniffer-DJYupBeK.cjs.map → sniffer-IAQEpsQb.cjs.map} +1 -1
- package/dist/{sst-DAqUoZKG.cjs → sst-BgcOsaBi.cjs} +3 -3
- package/dist/{sst-DAqUoZKG.cjs.map → sst-BgcOsaBi.cjs.map} +1 -1
- package/dist/{sst-D1Qj4sIs.mjs → sst-CJzvyrEz.mjs} +3 -3
- package/dist/{sst-D1Qj4sIs.mjs.map → sst-CJzvyrEz.mjs.map} +1 -1
- package/dist/{stages-Brn3vcdy.cjs → stages-CUn6gvXL.cjs} +2 -2
- package/dist/{stages-Brn3vcdy.cjs.map → stages-CUn6gvXL.cjs.map} +1 -1
- package/dist/{stages-DoHHz6pS.mjs → stages-CnZRYmkI.mjs} +2 -2
- package/dist/{stages-DoHHz6pS.mjs.map → stages-CnZRYmkI.mjs.map} +1 -1
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.d.cts +2 -2
- package/dist/target/public.d.mts +2 -2
- package/dist/target/public.mjs +1 -1
- package/dist/{terminal-BTbZlrFV.mjs → terminal-D8TPK7o9.mjs} +3 -3
- package/dist/{terminal-BTbZlrFV.mjs.map → terminal-D8TPK7o9.mjs.map} +1 -1
- package/dist/{terminal-COv23heb.cjs → terminal-DuapRxh6.cjs} +2 -2
- package/dist/{terminal-COv23heb.cjs.map → terminal-DuapRxh6.cjs.map} +1 -1
- package/dist/{types-0oAyFOfR.d.cts → types-NqX1Hz4P.d.cts} +210 -205
- package/dist/types-NqX1Hz4P.d.cts.map +1 -0
- package/dist/{types-BZ01tZs-.d.mts → types-ZHuZ89hv.d.mts} +210 -205
- package/dist/types-ZHuZ89hv.d.mts.map +1 -0
- package/dist/workspace/index.cjs +3 -1
- package/dist/workspace/index.d.cts +3 -3
- package/dist/workspace/index.d.mts +3 -3
- package/dist/workspace/index.mjs +2 -2
- package/dist/{workspace-D2CYLMCW.mjs → workspace-CJ0lKFq-.mjs} +231 -10
- package/dist/workspace-CJ0lKFq-.mjs.map +1 -0
- package/dist/{workspace-CT0JM3Y8.cjs → workspace-CwW3Gkru.cjs} +266 -9
- package/dist/workspace-CwW3Gkru.cjs.map +1 -0
- package/package.json +15 -15
- package/dist/add-5MZAXybv.cjs.map +0 -1
- package/dist/add-D_jNqnfZ.mjs.map +0 -1
- package/dist/cli-7edNFwGk.mjs.map +0 -1
- package/dist/cli-BL8ZHsLQ.cjs.map +0 -1
- package/dist/compose-DbJhRQlh.mjs.map +0 -1
- package/dist/compose-xw86kVYi.cjs.map +0 -1
- package/dist/credentials-BgJ5mS_d.mjs.map +0 -1
- package/dist/credentials-tr26-KML.cjs.map +0 -1
- package/dist/deploy-BTfmXd3g.mjs.map +0 -1
- package/dist/deploy-CtbZj4zN.cjs.map +0 -1
- package/dist/engine-Bt5k_UMq.cjs.map +0 -1
- package/dist/engine-jAINgiOB.mjs.map +0 -1
- package/dist/index-MnUJuUlj.d.mts.map +0 -1
- package/dist/index-elsLJTUv.d.cts.map +0 -1
- package/dist/seeds-BYdrtZsC.mjs.map +0 -1
- package/dist/types-0oAyFOfR.d.cts.map +0 -1
- package/dist/types-BZ01tZs-.d.mts.map +0 -1
- package/dist/workspace-CT0JM3Y8.cjs.map +0 -1
- package/dist/workspace-D2CYLMCW.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sst-DAqUoZKG.cjs","names":["join","rm","existsSync","readFile","kebabCase","appKey","CommandFailed","defineTarget","existsSync","join","MissingCredential","activeSandbox","LocalSandbox","detectPackageManager"],"sources":["../src/target/sst/health.ts","../src/target/sst/outputs.ts","../src/target/sst/index.ts"],"sourcesContent":["/**\n * Whether a released surface answers.\n *\n * The paths are the ones the local stack's healthchecks use\n * (`reconcile/apps.ts`): an API's `/health`, a site's `/`. A surface is\n * asked a few times, because a fresh deploy can take a moment to answer — a\n * Lambda's first start, a distribution still settling — and healthy means a\n * 2xx, after redirects.\n */\n\nimport type { TargetEvent } from '../types';\n\nexport interface HealthCheckOptions {\n\t/** How many times a surface is asked before it counts as down. */\n\tattempts: number;\n\t/** The wait between two attempts. */\n\tdelayMs: number;\n\t/** How long one request may take. */\n\ttimeoutMs: number;\n\tsignal: AbortSignal;\n\temit: (event: TargetEvent) => void;\n}\n\n/** Surfaces that never answered healthy. */\nexport class SurfacesUnhealthy extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\t/** Each surface that failed, with the last thing it answered. */\n\t\treadonly surfaces: readonly {\n\t\t\tapp: string;\n\t\t\turl: string;\n\t\t\tstatus?: number;\n\t\t\terror?: string;\n\t\t}[],\n\t) {\n\t\tconst lines = surfaces.map(\n\t\t\t({ app, url, status, error }) =>\n\t\t\t\t` - ${app}: ${url} → ${status ?? error ?? 'no answer'}`,\n\t\t);\n\t\tsuper(\n\t\t\t`The \"${stage}\" stage was deployed, but ${surfaces.length === 1 ? 'a surface does' : 'surfaces do'} not answer healthy:\\n${lines.join('\\n')}\\nCheck its logs in CloudWatch, fix it, and deploy again.`,\n\t\t);\n\t\tthis.name = 'SurfacesUnhealthy';\n\t}\n}\n\n/** `path` under `base`, keeping any path `base` already has. */\nexport function healthUrl(base: string, path: string): string {\n\tconst root = base.endsWith('/') ? base : `${base}/`;\n\treturn new URL(path.replace(/^\\//, ''), root).toString();\n}\n\n/**\n * Ask `url` until it answers 2xx or the attempts run out, emitting\n * `health.checked` for each. Resolves with the last answer.\n */\nexport async function checkHealth(\n\tapp: string,\n\turl: string,\n\toptions: HealthCheckOptions,\n): Promise<{ healthy: boolean; status?: number; error?: string }> {\n\tlet last: { healthy: boolean; status?: number; error?: string } = {\n\t\thealthy: false,\n\t};\n\tfor (let attempt = 1; attempt <= options.attempts; attempt++) {\n\t\toptions.signal.throwIfAborted();\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tredirect: 'follow',\n\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\toptions.signal,\n\t\t\t\t\tAbortSignal.timeout(options.timeoutMs),\n\t\t\t\t]),\n\t\t\t});\n\t\t\t// Drained, so the connection is not held open by an unread body.\n\t\t\tawait response.arrayBuffer().catch(() => undefined);\n\t\t\tlast = { healthy: response.ok, status: response.status };\n\t\t} catch (error) {\n\t\t\t// The run being stopped is not the surface being down.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tlast = {\n\t\t\t\thealthy: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t\toptions.emit({\n\t\t\ttype: 'health.checked',\n\t\t\tapp,\n\t\t\turl,\n\t\t\thealthy: last.healthy,\n\t\t\t...(last.status !== undefined ? { status: last.status } : {}),\n\t\t\tattempt,\n\t\t});\n\t\tif (last.healthy) return last;\n\t\tif (attempt < options.attempts) await wait(options.delayMs, options.signal);\n\t}\n\treturn last;\n}\n\nfunction wait(ms: number, signal: AbortSignal): Promise<void> {\n\tif (ms <= 0) return Promise.resolve();\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal.removeEventListener('abort', stop);\n\t\t\tresolve();\n\t\t}, ms);\n\t\tconst stop = () => {\n\t\t\tclearTimeout(timer);\n\t\t\treject(signal.reason);\n\t\t};\n\t\tsignal.addEventListener('abort', stop, { once: true });\n\t});\n}\n","/**\n * The surface URLs an `sst deploy` reports.\n *\n * SST writes whatever `run()` in `sst.config.ts` returns to\n * `.sst/outputs.json` after every successful deploy — `{\"Api\": \"https://…\"}`.\n * What `run()` returns is the project's to write, so this reads it leniently:\n * nested objects are walked, anything that is not an http(s) URL is ignored,\n * and an output names a surface by its id in any casing, with or without a\n * `Url` suffix (`Api`, `api`, `ApiUrl`, `API_URL`, `Api.url`).\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { kebabCase } from '@geekmidas/manifest';\n\n/** Where SST leaves the last successful deploy's outputs, under the project. */\nexport const SST_OUTPUTS_FILE = join('.sst', 'outputs.json');\n\n/** `.sst/outputs.json` is there, and is not JSON. */\nexport class SstOutputsUnreadable extends Error {\n\tconstructor(\n\t\treadonly path: string,\n\t\treadonly reason: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not read SST's outputs at ${path}: ${reason}. It is rewritten by every successful \\`sst deploy\\`; delete it and deploy again.`,\n\t\t);\n\t\tthis.name = 'SstOutputsUnreadable';\n\t}\n}\n\n/**\n * Remove what an earlier deploy wrote, so the outputs read after this one\n * are this one's — not another stage's, deployed from the same checkout.\n */\nexport async function clearOutputs(cwd: string): Promise<void> {\n\tawait rm(join(cwd, SST_OUTPUTS_FILE), { force: true });\n}\n\n/** The deploy's outputs, or `{}` when it wrote none. */\nexport async function readOutputs(cwd: string): Promise<unknown> {\n\tconst path = join(cwd, SST_OUTPUTS_FILE);\n\tif (!existsSync(path)) return {};\n\ttry {\n\t\treturn JSON.parse(await readFile(path, 'utf8'));\n\t} catch (error) {\n\t\tthrow new SstOutputsUnreadable(\n\t\t\tpath,\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t);\n\t}\n}\n\n/** Every http(s) URL in `outputs`, by its key path: `['Api', 'url']`. */\nfunction urlsIn(\n\tvalue: unknown,\n\tpath: string[] = [],\n\tfound: { path: string[]; url: string }[] = [],\n): { path: string[]; url: string }[] {\n\tif (typeof value === 'string') {\n\t\tif (isHttpUrl(value)) found.push({ path, url: value });\n\t} else if (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\tfor (const [key, child] of Object.entries(value)) {\n\t\t\turlsIn(child, [...path, key], found);\n\t\t}\n\t}\n\treturn found;\n}\n\nfunction isHttpUrl(value: string): boolean {\n\ttry {\n\t\tconst { protocol } = new URL(value);\n\t\treturn protocol === 'http:' || protocol === 'https:';\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * The app an output's key path names, kebab-cased as app keys are: its last\n * segment, without a `url` suffix — or its parent's, when the last segment\n * is only `url`. Exact names are tried first, so an app called `api-url`\n * still finds `ApiUrl`.\n */\nfunction namesOf(path: string[]): string[] {\n\tconst names: string[] = [];\n\tfor (const segment of [...path].reverse()) {\n\t\tconst name = kebabCase(segment);\n\t\tnames.push(name);\n\t\tconst bare = name.replace(/-?url$/, '');\n\t\tif (bare) {\n\t\t\tnames.push(bare);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn names;\n}\n\n/**\n * Each app's URL from the outputs, for the apps given. An app no output\n * names is left out.\n */\nexport function surfaceUrls(\n\toutputs: unknown,\n\tapps: readonly string[],\n): Record<string, string> {\n\tconst found = urlsIn(outputs);\n\tconst urls: Record<string, string> = {};\n\tfor (const app of apps) {\n\t\t// An exact name beats a stripped one, wherever each is in the file.\n\t\tconst exact = found.find(({ path }) => namesOf(path)[0] === app);\n\t\tconst loose = found.find(({ path }) => namesOf(path).includes(app));\n\t\tconst match = exact ?? loose;\n\t\tif (match) urls[app] = match.url;\n\t}\n\treturn urls;\n}\n","/**\n * The built-in `sst` target: `gkm build` writes the manifest, `sst deploy`\n * provisions it, and the surfaces SST reports are health-checked.\n *\n * `@geekmidas/cloud` is unchanged by it: `sst.config.ts` still reads\n * `.gkm/manifest/aws.ts` and `fromManifest` still makes every resource. What\n * the target adds is the run around the two commands — the lock, the events,\n * verification, and credentials kept where they belong.\n *\n * Both commands run in the sandbox: the build executes the project's code,\n * and so does `sst deploy`, which evaluates `sst.config.ts`. Only the second\n * needs AWS, so the AWS credential goes into that one command's environment\n * and nowhere else — not the build's, not the process the deploy runs in.\n * What `sst deploy` needs beyond them, the sandbox already passes: `PATH`\n * for node and the package manager, and `HOME`, under which SST keeps its\n * toolchain (`~/.config/sst`: bun, pulumi and its plugins) and the AWS SDK\n * finds `~/.aws` for a profile. Its state — the Pulumi backend and the\n * passphrase — is in the stage's own account, reached with that credential.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport {\n\ttype AwsCredential,\n\tMissingCredential,\n} from '../../deploy/credentials';\nimport type { DeployResult } from '../../deploy/types';\nimport { detectPackageManager, type PackageManager } from '../../init/utils';\nimport { CommandFailed } from '../../run';\nimport { LocalSandbox } from '../../sandbox/local';\nimport { activeSandbox, type Sandbox } from '../../sandbox/sandbox';\nimport { appKey } from '../../workspace/derive';\nimport { defineTarget } from '../define';\nimport type { DeployPhaseContext, DeployTarget } from '../types';\nimport { checkHealth, healthUrl, SurfacesUnhealthy } from './health';\nimport { clearOutputs, readOutputs, surfaceUrls } from './outputs';\n\nexport { SurfacesUnhealthy } from './health';\nexport { SST_OUTPUTS_FILE, SstOutputsUnreadable } from './outputs';\n\n/** A cold build of every handler and site. */\nconst BUILD_TIMEOUT_MS = 30 * 60_000;\n/**\n * A first deploy creates CloudFront distributions and, with a database, a\n * VPC and an RDS instance — each of which alone can take a quarter of an hour.\n */\nconst DEPLOY_TIMEOUT_MS = 90 * 60_000;\n\n/** The workspace has no `sst.config.ts` for `sst deploy` to run. */\nexport class SstConfigNotFound extends Error {\n\tconstructor(readonly cwd: string) {\n\t\tsuper(\n\t\t\t`No sst.config.ts in ${cwd}. The sst target runs \\`sst deploy\\` at the workspace root: scaffold one with \\`gkm init --deploy sst\\` and copy its sst.config.ts, or deploy through another target with --target.`,\n\t\t);\n\t\tthis.name = 'SstConfigNotFound';\n\t}\n}\n\n/** What the phases hand on. */\nexport interface SstRun {\n\tsandbox: Sandbox;\n\tpm: PackageManager;\n\taws: AwsCredential;\n\t/** The apps SST deploys this run: `ctx.apps` without the skipped. */\n\tapps: string[];\n\t/** Each app's URL, from the deploy's outputs. */\n\turls: Record<string, string>;\n}\n\nexport interface SstTargetOptions {\n\t/** How often, and how patiently, `verify` asks each surface. */\n\thealth?: { attempts?: number; delayMs?: number; timeoutMs?: number };\n}\n\n/**\n * The project's own copy of `bin`, through its package manager — never one\n * `npx` would download, which would be code nobody installed.\n */\nexport function projectCommand(\n\tpm: PackageManager,\n\tbin: string,\n\targs: readonly string[],\n): [command: string, args: string[]] {\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn ['pnpm', ['exec', bin, ...args]];\n\t\tcase 'yarn':\n\t\t\treturn ['yarn', [bin, ...args]];\n\t\tcase 'bun':\n\t\t\treturn ['bunx', [bin, ...args]];\n\t\tcase 'npm':\n\t\t\treturn ['npx', ['--no-install', bin, ...args]];\n\t}\n}\n\n/**\n * The variables `credential` is to the AWS SDK and to SST.\n *\n * A profile is passed alone: given keys too, every SDK would take the keys.\n */\nexport function awsEnv(credential: AwsCredential): Record<string, string> {\n\tconst region: Record<string, string> = credential.region\n\t\t? { AWS_REGION: credential.region, AWS_DEFAULT_REGION: credential.region }\n\t\t: {};\n\tif ('profile' in credential) {\n\t\treturn { AWS_PROFILE: credential.profile, ...region };\n\t}\n\treturn {\n\t\tAWS_ACCESS_KEY_ID: credential.accessKeyId,\n\t\tAWS_SECRET_ACCESS_KEY: credential.secretAccessKey,\n\t\t...(credential.sessionToken\n\t\t\t? { AWS_SESSION_TOKEN: credential.sessionToken }\n\t\t\t: {}),\n\t\t...region,\n\t};\n}\n\n/**\n * `env` without any `AWS_*`, so a host sandbox that passes some cannot put\n * a second account beside the one the credential names.\n */\nfunction withoutAws(\n\tenv: Readonly<Record<string, string>>,\n): Record<string, string> {\n\treturn Object.fromEntries(\n\t\tObject.entries(env).filter(([name]) => !name.startsWith('AWS_')),\n\t);\n}\n\n/** Each app's kind in the manifest, by app key: `rest-api` or `site`. */\nfunction kindsOf(manifest: ConstructManifest): Map<string, string> {\n\treturn new Map(\n\t\tObject.entries(manifest).map(([id, declaration]) => [\n\t\t\tappKey(id),\n\t\t\tdeclaration.kind,\n\t\t]),\n\t);\n}\n\nasync function exec(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n\t[command, args]: [string, string[]],\n\toptions: { env: Record<string, string>; timeoutMs: number },\n): Promise<void> {\n\tconst result = await run.sandbox.exec(command, args, {\n\t\tcwd: ctx.cwd,\n\t\tenv: options.env,\n\t\ttimeoutMs: options.timeoutMs,\n\t\toutput: ctx.childOutput,\n\t\tsignal: ctx.signal,\n\t});\n\tif (result.exitCode !== 0) {\n\t\tthrow new CommandFailed(\n\t\t\tcommand,\n\t\t\targs,\n\t\t\tresult.exitCode,\n\t\t\tresult.signal as NodeJS.Signals | null,\n\t\t);\n\t}\n}\n\n/** The `sst` target, with its health checks tuned — for tests and hosts. */\nexport function createSstTarget(\n\toptions: SstTargetOptions = {},\n): DeployTarget<undefined, SstRun> {\n\tconst health = {\n\t\tattempts: options.health?.attempts ?? 6,\n\t\tdelayMs: options.health?.delayMs ?? 5_000,\n\t\ttimeoutMs: options.health?.timeoutMs ?? 10_000,\n\t};\n\n\treturn defineTarget<undefined, SstRun>({\n\t\tname: 'sst',\n\t\truntime: 'aws',\n\t\t// SST keeps no previous release to return to; migrations run in the\n\t\t// stack, as `fromManifest` provisions the database; and nothing here\n\t\t// builds an image.\n\t\tcapabilities: { rollback: false, migrations: 'target', images: false },\n\t\tcredentials: ['aws'],\n\n\t\tasync validate(ctx) {\n\t\t\tif (!existsSync(join(ctx.cwd, 'sst.config.ts'))) {\n\t\t\t\tthrow new SstConfigNotFound(ctx.cwd);\n\t\t\t}\n\n\t\t\tconst aws = await ctx.credentials.get(\n\t\t\t\t{ kind: 'aws', stage: ctx.stage },\n\t\t\t\t{ signal: ctx.signal },\n\t\t\t);\n\t\t\tif (!aws) throw new MissingCredential('aws', ctx.stage);\n\t\t\tif (!('profile' in aws)) {\n\t\t\t\tctx.secrets.mask(aws.secretAccessKey);\n\t\t\t\tif (aws.sessionToken) ctx.secrets.mask(aws.sessionToken);\n\t\t\t}\n\n\t\t\t// A mobile app ships through its own store's toolchain.\n\t\t\tfor (const app of ctx.apps) {\n\t\t\t\tif (ctx.workspace.apps[app]?.type === 'mobile') {\n\t\t\t\t\tctx.skip(app, 'mobile apps deploy through their own toolchain');\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst skipped = new Set(ctx.skipped.map(({ app }) => app));\n\t\t\tconst apps = ctx.apps.filter((app) => !skipped.has(app));\n\n\t\t\tctx.logger.info(\n\t\t\t\t`\\n🚀 Deploying \"${ctx.workspace.name}\" to the \"${ctx.stage}\" stage with SST`,\n\t\t\t);\n\t\t\tctx.logger.info(\n\t\t\t\t` AWS: ${'profile' in aws ? `profile \"${aws.profile}\"` : 'access keys'}`,\n\t\t\t);\n\t\t\t// `sst deploy` takes the whole stack: there is no deploying one app.\n\t\t\tconst deployable = Object.values(ctx.workspace.apps).filter(\n\t\t\t\t(app) => app.type !== 'mobile',\n\t\t\t).length;\n\t\t\tif (apps.length < deployable) {\n\t\t\t\tctx.logger.warn(\n\t\t\t\t\t` sst deploy deploys every construct of the stage, not only ${apps.join(', ')}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsandbox: activeSandbox() ?? new LocalSandbox({ root: ctx.cwd }),\n\t\t\t\tpm: detectPackageManager(ctx.cwd),\n\t\t\t\taws,\n\t\t\t\tapps,\n\t\t\t\turls: {},\n\t\t\t};\n\t\t},\n\n\t\tasync plan(ctx, run) {\n\t\t\tconst kinds = kindsOf(ctx.manifest);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'resource.planned',\n\t\t\t\t\tkey: `application:${app}`,\n\t\t\t\t\tresourceType: kinds.get(app) ?? 'application',\n\t\t\t\t\taction: 'ensure',\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst [build, buildArgs] = buildCommand(ctx, run);\n\t\t\tconst [deploy, deployArgs] = deployCommand(ctx, run);\n\t\t\tctx.logger.info(` Would run: ${[build, ...buildArgs].join(' ')}`);\n\t\t\tctx.logger.info(` Would run: ${[deploy, ...deployArgs].join(' ')}`);\n\t\t},\n\n\t\tasync build(ctx, run) {\n\t\t\tctx.logger.info('\\n📦 Building the manifest SST deploys');\n\t\t\t// No credential: the build runs the project's code and needs none.\n\t\t\tawait exec(ctx, run, buildCommand(ctx, run), {\n\t\t\t\tenv: { ...run.sandbox.env },\n\t\t\t\ttimeoutMs: BUILD_TIMEOUT_MS,\n\t\t\t});\n\t\t},\n\n\t\tasync release(ctx, run) {\n\t\t\tctx.logger.info(`\\n☁️ sst deploy --stage ${ctx.stage}`);\n\t\t\tawait clearOutputs(ctx.cwd);\n\t\t\tawait exec(ctx, run, deployCommand(ctx, run), {\n\t\t\t\tenv: { ...withoutAws(run.sandbox.env), ...awsEnv(run.aws) },\n\t\t\t\ttimeoutMs: DEPLOY_TIMEOUT_MS,\n\t\t\t});\n\n\t\t\trun.urls = surfaceUrls(await readOutputs(ctx.cwd), run.apps);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'app.deployed',\n\t\t\t\t\tapp,\n\t\t\t\t\tapplicationId: ctx.name(app),\n\t\t\t\t\t// SST builds no image a registry holds.\n\t\t\t\t\timageRef: '',\n\t\t\t\t\turl: run.urls[app] ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\n\t\tasync verify(ctx, run) {\n\t\t\tconst unhealthy: SurfacesUnhealthy['surfaces'][number][] = [];\n\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tconst base = run.urls[app];\n\t\t\t\tif (!base) {\n\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t` ⚠ No URL for ${app} in SST's outputs, so it was not checked. Return it from run() in sst.config.ts — { ${app}: <its url> } — to have it checked.`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// A surface — a `rest-api` — is the backend app it derives.\n\t\t\t\tconst backend = ctx.workspace.apps[app]?.type === 'backend';\n\t\t\t\tconst url = healthUrl(base, backend ? '/health' : '/');\n\t\t\t\tconst answer = await checkHealth(app, url, {\n\t\t\t\t\t...health,\n\t\t\t\t\tsignal: ctx.signal,\n\t\t\t\t\temit: ctx.emit,\n\t\t\t\t});\n\t\t\t\tif (answer.healthy) {\n\t\t\t\t\tctx.logger.info(` ✓ ${app}: ${url}`);\n\t\t\t\t} else {\n\t\t\t\t\tunhealthy.push({\n\t\t\t\t\t\tapp,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\t...(answer.status !== undefined ? { status: answer.status } : {}),\n\t\t\t\t\t\t...(answer.error ? { error: answer.error } : {}),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (unhealthy.length > 0) {\n\t\t\t\tthrow new SurfacesUnhealthy(ctx.stage, unhealthy);\n\t\t\t}\n\t\t},\n\n\t\tresult(ctx, run): DeployResult {\n\t\t\treturn {\n\t\t\t\tapps: run.apps.map((appName) => ({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: ctx.workspace.apps[appName]?.type ?? 'backend',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\t...(run.urls[appName] ? { url: run.urls[appName] } : {}),\n\t\t\t\t})),\n\t\t\t\tprojectId: '',\n\t\t\t\tenvironmentId: '',\n\t\t\t\tsuccessCount: ctx.dryRun ? 0 : run.apps.length,\n\t\t\t\tfailedCount: 0,\n\t\t\t\tstage: ctx.stage,\n\t\t\t\tidentity: ctx.identity.key,\n\t\t\t\ttag: ctx.tag,\n\t\t\t\tdryRun: ctx.dryRun,\n\t\t\t\tskipped: [...ctx.skipped],\n\t\t\t\turls: { ...run.urls },\n\t\t\t\tchanges: [],\n\t\t\t};\n\t\t},\n\t});\n}\n\nfunction buildCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'gkm', [\n\t\t'build',\n\t\t'--provider',\n\t\t'aws',\n\t\t'--stage',\n\t\tctx.stage,\n\t]);\n}\n\nfunction deployCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'sst', ['deploy', '--stage', ctx.stage]);\n}\n\nexport const sstTarget = createSstTarget();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sst-BgcOsaBi.cjs","names":["join","rm","existsSync","readFile","kebabCase","appKey","CommandFailed","defineTarget","existsSync","join","MissingCredential","activeSandbox","LocalSandbox","detectPackageManager"],"sources":["../src/target/sst/health.ts","../src/target/sst/outputs.ts","../src/target/sst/index.ts"],"sourcesContent":["/**\n * Whether a released surface answers.\n *\n * The paths are the ones the local stack's healthchecks use\n * (`reconcile/apps.ts`): an API's `/health`, a site's `/`. A surface is\n * asked a few times, because a fresh deploy can take a moment to answer — a\n * Lambda's first start, a distribution still settling — and healthy means a\n * 2xx, after redirects.\n */\n\nimport type { TargetEvent } from '../types';\n\nexport interface HealthCheckOptions {\n\t/** How many times a surface is asked before it counts as down. */\n\tattempts: number;\n\t/** The wait between two attempts. */\n\tdelayMs: number;\n\t/** How long one request may take. */\n\ttimeoutMs: number;\n\tsignal: AbortSignal;\n\temit: (event: TargetEvent) => void;\n}\n\n/** Surfaces that never answered healthy. */\nexport class SurfacesUnhealthy extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\t/** Each surface that failed, with the last thing it answered. */\n\t\treadonly surfaces: readonly {\n\t\t\tapp: string;\n\t\t\turl: string;\n\t\t\tstatus?: number;\n\t\t\terror?: string;\n\t\t}[],\n\t) {\n\t\tconst lines = surfaces.map(\n\t\t\t({ app, url, status, error }) =>\n\t\t\t\t` - ${app}: ${url} → ${status ?? error ?? 'no answer'}`,\n\t\t);\n\t\tsuper(\n\t\t\t`The \"${stage}\" stage was deployed, but ${surfaces.length === 1 ? 'a surface does' : 'surfaces do'} not answer healthy:\\n${lines.join('\\n')}\\nCheck its logs in CloudWatch, fix it, and deploy again.`,\n\t\t);\n\t\tthis.name = 'SurfacesUnhealthy';\n\t}\n}\n\n/** `path` under `base`, keeping any path `base` already has. */\nexport function healthUrl(base: string, path: string): string {\n\tconst root = base.endsWith('/') ? base : `${base}/`;\n\treturn new URL(path.replace(/^\\//, ''), root).toString();\n}\n\n/**\n * Ask `url` until it answers 2xx or the attempts run out, emitting\n * `health.checked` for each. Resolves with the last answer.\n */\nexport async function checkHealth(\n\tapp: string,\n\turl: string,\n\toptions: HealthCheckOptions,\n): Promise<{ healthy: boolean; status?: number; error?: string }> {\n\tlet last: { healthy: boolean; status?: number; error?: string } = {\n\t\thealthy: false,\n\t};\n\tfor (let attempt = 1; attempt <= options.attempts; attempt++) {\n\t\toptions.signal.throwIfAborted();\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tredirect: 'follow',\n\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\toptions.signal,\n\t\t\t\t\tAbortSignal.timeout(options.timeoutMs),\n\t\t\t\t]),\n\t\t\t});\n\t\t\t// Drained, so the connection is not held open by an unread body.\n\t\t\tawait response.arrayBuffer().catch(() => undefined);\n\t\t\tlast = { healthy: response.ok, status: response.status };\n\t\t} catch (error) {\n\t\t\t// The run being stopped is not the surface being down.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tlast = {\n\t\t\t\thealthy: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t\toptions.emit({\n\t\t\ttype: 'health.checked',\n\t\t\tapp,\n\t\t\turl,\n\t\t\thealthy: last.healthy,\n\t\t\t...(last.status !== undefined ? { status: last.status } : {}),\n\t\t\tattempt,\n\t\t});\n\t\tif (last.healthy) return last;\n\t\tif (attempt < options.attempts) await wait(options.delayMs, options.signal);\n\t}\n\treturn last;\n}\n\nfunction wait(ms: number, signal: AbortSignal): Promise<void> {\n\tif (ms <= 0) return Promise.resolve();\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal.removeEventListener('abort', stop);\n\t\t\tresolve();\n\t\t}, ms);\n\t\tconst stop = () => {\n\t\t\tclearTimeout(timer);\n\t\t\treject(signal.reason);\n\t\t};\n\t\tsignal.addEventListener('abort', stop, { once: true });\n\t});\n}\n","/**\n * The surface URLs an `sst deploy` reports.\n *\n * SST writes whatever `run()` in `sst.config.ts` returns to\n * `.sst/outputs.json` after every successful deploy — `{\"Api\": \"https://…\"}`.\n * What `run()` returns is the project's to write, so this reads it leniently:\n * nested objects are walked, anything that is not an http(s) URL is ignored,\n * and an output names a surface by its id in any casing, with or without a\n * `Url` suffix (`Api`, `api`, `ApiUrl`, `API_URL`, `Api.url`).\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { kebabCase } from '@geekmidas/manifest';\n\n/** Where SST leaves the last successful deploy's outputs, under the project. */\nexport const SST_OUTPUTS_FILE = join('.sst', 'outputs.json');\n\n/** `.sst/outputs.json` is there, and is not JSON. */\nexport class SstOutputsUnreadable extends Error {\n\tconstructor(\n\t\treadonly path: string,\n\t\treadonly reason: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not read SST's outputs at ${path}: ${reason}. It is rewritten by every successful \\`sst deploy\\`; delete it and deploy again.`,\n\t\t);\n\t\tthis.name = 'SstOutputsUnreadable';\n\t}\n}\n\n/**\n * Remove what an earlier deploy wrote, so the outputs read after this one\n * are this one's — not another stage's, deployed from the same checkout.\n */\nexport async function clearOutputs(cwd: string): Promise<void> {\n\tawait rm(join(cwd, SST_OUTPUTS_FILE), { force: true });\n}\n\n/** The deploy's outputs, or `{}` when it wrote none. */\nexport async function readOutputs(cwd: string): Promise<unknown> {\n\tconst path = join(cwd, SST_OUTPUTS_FILE);\n\tif (!existsSync(path)) return {};\n\ttry {\n\t\treturn JSON.parse(await readFile(path, 'utf8'));\n\t} catch (error) {\n\t\tthrow new SstOutputsUnreadable(\n\t\t\tpath,\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t);\n\t}\n}\n\n/** Every http(s) URL in `outputs`, by its key path: `['Api', 'url']`. */\nfunction urlsIn(\n\tvalue: unknown,\n\tpath: string[] = [],\n\tfound: { path: string[]; url: string }[] = [],\n): { path: string[]; url: string }[] {\n\tif (typeof value === 'string') {\n\t\tif (isHttpUrl(value)) found.push({ path, url: value });\n\t} else if (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\tfor (const [key, child] of Object.entries(value)) {\n\t\t\turlsIn(child, [...path, key], found);\n\t\t}\n\t}\n\treturn found;\n}\n\nfunction isHttpUrl(value: string): boolean {\n\ttry {\n\t\tconst { protocol } = new URL(value);\n\t\treturn protocol === 'http:' || protocol === 'https:';\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * The app an output's key path names, kebab-cased as app keys are: its last\n * segment, without a `url` suffix — or its parent's, when the last segment\n * is only `url`. Exact names are tried first, so an app called `api-url`\n * still finds `ApiUrl`.\n */\nfunction namesOf(path: string[]): string[] {\n\tconst names: string[] = [];\n\tfor (const segment of [...path].reverse()) {\n\t\tconst name = kebabCase(segment);\n\t\tnames.push(name);\n\t\tconst bare = name.replace(/-?url$/, '');\n\t\tif (bare) {\n\t\t\tnames.push(bare);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn names;\n}\n\n/**\n * Each app's URL from the outputs, for the apps given. An app no output\n * names is left out.\n */\nexport function surfaceUrls(\n\toutputs: unknown,\n\tapps: readonly string[],\n): Record<string, string> {\n\tconst found = urlsIn(outputs);\n\tconst urls: Record<string, string> = {};\n\tfor (const app of apps) {\n\t\t// An exact name beats a stripped one, wherever each is in the file.\n\t\tconst exact = found.find(({ path }) => namesOf(path)[0] === app);\n\t\tconst loose = found.find(({ path }) => namesOf(path).includes(app));\n\t\tconst match = exact ?? loose;\n\t\tif (match) urls[app] = match.url;\n\t}\n\treturn urls;\n}\n","/**\n * The built-in `sst` target: `gkm build` writes the manifest, `sst deploy`\n * provisions it, and the surfaces SST reports are health-checked.\n *\n * `@geekmidas/cloud` is unchanged by it: `sst.config.ts` still reads\n * `.gkm/manifest/aws.ts` and `fromManifest` still makes every resource. What\n * the target adds is the run around the two commands — the lock, the events,\n * verification, and credentials kept where they belong.\n *\n * Both commands run in the sandbox: the build executes the project's code,\n * and so does `sst deploy`, which evaluates `sst.config.ts`. Only the second\n * needs AWS, so the AWS credential goes into that one command's environment\n * and nowhere else — not the build's, not the process the deploy runs in.\n * What `sst deploy` needs beyond them, the sandbox already passes: `PATH`\n * for node and the package manager, and `HOME`, under which SST keeps its\n * toolchain (`~/.config/sst`: bun, pulumi and its plugins) and the AWS SDK\n * finds `~/.aws` for a profile. Its state — the Pulumi backend and the\n * passphrase — is in the stage's own account, reached with that credential.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport {\n\ttype AwsCredential,\n\tMissingCredential,\n} from '../../deploy/credentials';\nimport type { DeployResult } from '../../deploy/types';\nimport { detectPackageManager, type PackageManager } from '../../init/utils';\nimport { CommandFailed } from '../../run';\nimport { LocalSandbox } from '../../sandbox/local';\nimport { activeSandbox, type Sandbox } from '../../sandbox/sandbox';\nimport { appKey } from '../../workspace/derive';\nimport { defineTarget } from '../define';\nimport type { DeployPhaseContext, DeployTarget } from '../types';\nimport { checkHealth, healthUrl, SurfacesUnhealthy } from './health';\nimport { clearOutputs, readOutputs, surfaceUrls } from './outputs';\n\nexport { SurfacesUnhealthy } from './health';\nexport { SST_OUTPUTS_FILE, SstOutputsUnreadable } from './outputs';\n\n/** A cold build of every handler and site. */\nconst BUILD_TIMEOUT_MS = 30 * 60_000;\n/**\n * A first deploy creates CloudFront distributions and, with a database, a\n * VPC and an RDS instance — each of which alone can take a quarter of an hour.\n */\nconst DEPLOY_TIMEOUT_MS = 90 * 60_000;\n\n/** The workspace has no `sst.config.ts` for `sst deploy` to run. */\nexport class SstConfigNotFound extends Error {\n\tconstructor(readonly cwd: string) {\n\t\tsuper(\n\t\t\t`No sst.config.ts in ${cwd}. The sst target runs \\`sst deploy\\` at the workspace root: scaffold one with \\`gkm init --deploy sst\\` and copy its sst.config.ts, or deploy through another target with --target.`,\n\t\t);\n\t\tthis.name = 'SstConfigNotFound';\n\t}\n}\n\n/** What the phases hand on. */\nexport interface SstRun {\n\tsandbox: Sandbox;\n\tpm: PackageManager;\n\taws: AwsCredential;\n\t/** The apps SST deploys this run: `ctx.apps` without the skipped. */\n\tapps: string[];\n\t/** Each app's URL, from the deploy's outputs. */\n\turls: Record<string, string>;\n}\n\nexport interface SstTargetOptions {\n\t/** How often, and how patiently, `verify` asks each surface. */\n\thealth?: { attempts?: number; delayMs?: number; timeoutMs?: number };\n}\n\n/**\n * The project's own copy of `bin`, through its package manager — never one\n * `npx` would download, which would be code nobody installed.\n */\nexport function projectCommand(\n\tpm: PackageManager,\n\tbin: string,\n\targs: readonly string[],\n): [command: string, args: string[]] {\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn ['pnpm', ['exec', bin, ...args]];\n\t\tcase 'yarn':\n\t\t\treturn ['yarn', [bin, ...args]];\n\t\tcase 'bun':\n\t\t\treturn ['bunx', [bin, ...args]];\n\t\tcase 'npm':\n\t\t\treturn ['npx', ['--no-install', bin, ...args]];\n\t}\n}\n\n/**\n * The variables `credential` is to the AWS SDK and to SST.\n *\n * A profile is passed alone: given keys too, every SDK would take the keys.\n */\nexport function awsEnv(credential: AwsCredential): Record<string, string> {\n\tconst region: Record<string, string> = credential.region\n\t\t? { AWS_REGION: credential.region, AWS_DEFAULT_REGION: credential.region }\n\t\t: {};\n\tif ('profile' in credential) {\n\t\treturn { AWS_PROFILE: credential.profile, ...region };\n\t}\n\treturn {\n\t\tAWS_ACCESS_KEY_ID: credential.accessKeyId,\n\t\tAWS_SECRET_ACCESS_KEY: credential.secretAccessKey,\n\t\t...(credential.sessionToken\n\t\t\t? { AWS_SESSION_TOKEN: credential.sessionToken }\n\t\t\t: {}),\n\t\t...region,\n\t};\n}\n\n/**\n * `env` without any `AWS_*`, so a host sandbox that passes some cannot put\n * a second account beside the one the credential names.\n */\nfunction withoutAws(\n\tenv: Readonly<Record<string, string>>,\n): Record<string, string> {\n\treturn Object.fromEntries(\n\t\tObject.entries(env).filter(([name]) => !name.startsWith('AWS_')),\n\t);\n}\n\n/** Each app's kind in the manifest, by app key: `rest-api` or `site`. */\nfunction kindsOf(manifest: ConstructManifest): Map<string, string> {\n\treturn new Map(\n\t\tObject.entries(manifest).map(([id, declaration]) => [\n\t\t\tappKey(id),\n\t\t\tdeclaration.kind,\n\t\t]),\n\t);\n}\n\nasync function exec(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n\t[command, args]: [string, string[]],\n\toptions: { env: Record<string, string>; timeoutMs: number },\n): Promise<void> {\n\tconst result = await run.sandbox.exec(command, args, {\n\t\tcwd: ctx.cwd,\n\t\tenv: options.env,\n\t\ttimeoutMs: options.timeoutMs,\n\t\toutput: ctx.childOutput,\n\t\tsignal: ctx.signal,\n\t});\n\tif (result.exitCode !== 0) {\n\t\tthrow new CommandFailed(\n\t\t\tcommand,\n\t\t\targs,\n\t\t\tresult.exitCode,\n\t\t\tresult.signal as NodeJS.Signals | null,\n\t\t);\n\t}\n}\n\n/** The `sst` target, with its health checks tuned — for tests and hosts. */\nexport function createSstTarget(\n\toptions: SstTargetOptions = {},\n): DeployTarget<undefined, SstRun> {\n\tconst health = {\n\t\tattempts: options.health?.attempts ?? 6,\n\t\tdelayMs: options.health?.delayMs ?? 5_000,\n\t\ttimeoutMs: options.health?.timeoutMs ?? 10_000,\n\t};\n\n\treturn defineTarget<undefined, SstRun>({\n\t\tname: 'sst',\n\t\truntime: 'aws',\n\t\t// SST keeps no previous release to return to; migrations run in the\n\t\t// stack, as `fromManifest` provisions the database; and nothing here\n\t\t// builds an image.\n\t\tcapabilities: { rollback: false, migrations: 'target', images: false },\n\t\tcredentials: ['aws'],\n\n\t\tasync validate(ctx) {\n\t\t\tif (!existsSync(join(ctx.cwd, 'sst.config.ts'))) {\n\t\t\t\tthrow new SstConfigNotFound(ctx.cwd);\n\t\t\t}\n\n\t\t\tconst aws = await ctx.credentials.get(\n\t\t\t\t{ kind: 'aws', stage: ctx.stage },\n\t\t\t\t{ signal: ctx.signal },\n\t\t\t);\n\t\t\tif (!aws) throw new MissingCredential('aws', ctx.stage);\n\t\t\tif (!('profile' in aws)) {\n\t\t\t\tctx.secrets.mask(aws.secretAccessKey);\n\t\t\t\tif (aws.sessionToken) ctx.secrets.mask(aws.sessionToken);\n\t\t\t}\n\n\t\t\t// A mobile app ships through its own store's toolchain.\n\t\t\tfor (const app of ctx.apps) {\n\t\t\t\tif (ctx.workspace.apps[app]?.type === 'mobile') {\n\t\t\t\t\tctx.skip(app, 'mobile apps deploy through their own toolchain');\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst skipped = new Set(ctx.skipped.map(({ app }) => app));\n\t\t\tconst apps = ctx.apps.filter((app) => !skipped.has(app));\n\n\t\t\tctx.logger.info(\n\t\t\t\t`\\n🚀 Deploying \"${ctx.workspace.name}\" to the \"${ctx.stage}\" stage with SST`,\n\t\t\t);\n\t\t\tctx.logger.info(\n\t\t\t\t` AWS: ${'profile' in aws ? `profile \"${aws.profile}\"` : 'access keys'}`,\n\t\t\t);\n\t\t\t// `sst deploy` takes the whole stack: there is no deploying one app.\n\t\t\tconst deployable = Object.values(ctx.workspace.apps).filter(\n\t\t\t\t(app) => app.type !== 'mobile',\n\t\t\t).length;\n\t\t\tif (apps.length < deployable) {\n\t\t\t\tctx.logger.warn(\n\t\t\t\t\t` sst deploy deploys every construct of the stage, not only ${apps.join(', ')}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsandbox: activeSandbox() ?? new LocalSandbox({ root: ctx.cwd }),\n\t\t\t\tpm: detectPackageManager(ctx.cwd),\n\t\t\t\taws,\n\t\t\t\tapps,\n\t\t\t\turls: {},\n\t\t\t};\n\t\t},\n\n\t\tasync plan(ctx, run) {\n\t\t\tconst kinds = kindsOf(ctx.manifest);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'resource.planned',\n\t\t\t\t\tkey: `application:${app}`,\n\t\t\t\t\tresourceType: kinds.get(app) ?? 'application',\n\t\t\t\t\taction: 'ensure',\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst [build, buildArgs] = buildCommand(ctx, run);\n\t\t\tconst [deploy, deployArgs] = deployCommand(ctx, run);\n\t\t\tctx.logger.info(` Would run: ${[build, ...buildArgs].join(' ')}`);\n\t\t\tctx.logger.info(` Would run: ${[deploy, ...deployArgs].join(' ')}`);\n\t\t},\n\n\t\tasync build(ctx, run) {\n\t\t\tctx.logger.info('\\n📦 Building the manifest SST deploys');\n\t\t\t// No credential: the build runs the project's code and needs none.\n\t\t\tawait exec(ctx, run, buildCommand(ctx, run), {\n\t\t\t\tenv: { ...run.sandbox.env },\n\t\t\t\ttimeoutMs: BUILD_TIMEOUT_MS,\n\t\t\t});\n\t\t},\n\n\t\tasync release(ctx, run) {\n\t\t\tctx.logger.info(`\\n☁️ sst deploy --stage ${ctx.stage}`);\n\t\t\tawait clearOutputs(ctx.cwd);\n\t\t\tawait exec(ctx, run, deployCommand(ctx, run), {\n\t\t\t\tenv: { ...withoutAws(run.sandbox.env), ...awsEnv(run.aws) },\n\t\t\t\ttimeoutMs: DEPLOY_TIMEOUT_MS,\n\t\t\t});\n\n\t\t\trun.urls = surfaceUrls(await readOutputs(ctx.cwd), run.apps);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'app.deployed',\n\t\t\t\t\tapp,\n\t\t\t\t\tapplicationId: ctx.name(app),\n\t\t\t\t\t// SST builds no image a registry holds.\n\t\t\t\t\timageRef: '',\n\t\t\t\t\turl: run.urls[app] ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\n\t\tasync verify(ctx, run) {\n\t\t\tconst unhealthy: SurfacesUnhealthy['surfaces'][number][] = [];\n\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tconst base = run.urls[app];\n\t\t\t\tif (!base) {\n\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t` ⚠ No URL for ${app} in SST's outputs, so it was not checked. Return it from run() in sst.config.ts — { ${app}: <its url> } — to have it checked.`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// A surface — a `rest-api` — is the backend app it derives.\n\t\t\t\tconst backend = ctx.workspace.apps[app]?.type === 'backend';\n\t\t\t\tconst url = healthUrl(base, backend ? '/health' : '/');\n\t\t\t\tconst answer = await checkHealth(app, url, {\n\t\t\t\t\t...health,\n\t\t\t\t\tsignal: ctx.signal,\n\t\t\t\t\temit: ctx.emit,\n\t\t\t\t});\n\t\t\t\tif (answer.healthy) {\n\t\t\t\t\tctx.logger.info(` ✓ ${app}: ${url}`);\n\t\t\t\t} else {\n\t\t\t\t\tunhealthy.push({\n\t\t\t\t\t\tapp,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\t...(answer.status !== undefined ? { status: answer.status } : {}),\n\t\t\t\t\t\t...(answer.error ? { error: answer.error } : {}),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (unhealthy.length > 0) {\n\t\t\t\tthrow new SurfacesUnhealthy(ctx.stage, unhealthy);\n\t\t\t}\n\t\t},\n\n\t\tresult(ctx, run): DeployResult {\n\t\t\treturn {\n\t\t\t\tapps: run.apps.map((appName) => ({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: ctx.workspace.apps[appName]?.type ?? 'backend',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\t...(run.urls[appName] ? { url: run.urls[appName] } : {}),\n\t\t\t\t})),\n\t\t\t\tprojectId: '',\n\t\t\t\tenvironmentId: '',\n\t\t\t\tsuccessCount: ctx.dryRun ? 0 : run.apps.length,\n\t\t\t\tfailedCount: 0,\n\t\t\t\tstage: ctx.stage,\n\t\t\t\tidentity: ctx.identity.key,\n\t\t\t\ttag: ctx.tag,\n\t\t\t\tdryRun: ctx.dryRun,\n\t\t\t\tskipped: [...ctx.skipped],\n\t\t\t\turls: { ...run.urls },\n\t\t\t\tchanges: [],\n\t\t\t};\n\t\t},\n\t});\n}\n\nfunction buildCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'gkm', [\n\t\t'build',\n\t\t'--provider',\n\t\t'aws',\n\t\t'--stage',\n\t\tctx.stage,\n\t]);\n}\n\nfunction deployCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'sst', ['deploy', '--stage', ctx.stage]);\n}\n\nexport const sstTarget = createSstTarget();\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
2
|
-
import { i as CommandFailed, t as MissingCredential } from "./credentials-
|
|
2
|
+
import { i as CommandFailed, t as MissingCredential } from "./credentials-CnVqoXMb.mjs";
|
|
3
3
|
import { i as activeSandbox } from "./sandbox-Dm247NGx.mjs";
|
|
4
4
|
import { t as appKey } from "./derive-B4vDpzgP.mjs";
|
|
5
5
|
import { t as defineTarget } from "./define-DR8xGYfL.mjs";
|
|
6
|
-
import { t as LocalSandbox } from "./local-
|
|
6
|
+
import { t as LocalSandbox } from "./local-IuoIF3CT.mjs";
|
|
7
7
|
import { n as detectPackageManager } from "./utils-D_fxUqvU.mjs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
@@ -427,4 +427,4 @@ const sstTarget = createSstTarget();
|
|
|
427
427
|
//#endregion
|
|
428
428
|
export { SurfacesUnhealthy as i, sst_exports as n, SstOutputsUnreadable as r, SstConfigNotFound as t };
|
|
429
429
|
|
|
430
|
-
//# sourceMappingURL=sst-
|
|
430
|
+
//# sourceMappingURL=sst-CJzvyrEz.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sst-D1Qj4sIs.mjs","names":[],"sources":["../src/target/sst/health.ts","../src/target/sst/outputs.ts","../src/target/sst/index.ts"],"sourcesContent":["/**\n * Whether a released surface answers.\n *\n * The paths are the ones the local stack's healthchecks use\n * (`reconcile/apps.ts`): an API's `/health`, a site's `/`. A surface is\n * asked a few times, because a fresh deploy can take a moment to answer — a\n * Lambda's first start, a distribution still settling — and healthy means a\n * 2xx, after redirects.\n */\n\nimport type { TargetEvent } from '../types';\n\nexport interface HealthCheckOptions {\n\t/** How many times a surface is asked before it counts as down. */\n\tattempts: number;\n\t/** The wait between two attempts. */\n\tdelayMs: number;\n\t/** How long one request may take. */\n\ttimeoutMs: number;\n\tsignal: AbortSignal;\n\temit: (event: TargetEvent) => void;\n}\n\n/** Surfaces that never answered healthy. */\nexport class SurfacesUnhealthy extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\t/** Each surface that failed, with the last thing it answered. */\n\t\treadonly surfaces: readonly {\n\t\t\tapp: string;\n\t\t\turl: string;\n\t\t\tstatus?: number;\n\t\t\terror?: string;\n\t\t}[],\n\t) {\n\t\tconst lines = surfaces.map(\n\t\t\t({ app, url, status, error }) =>\n\t\t\t\t` - ${app}: ${url} → ${status ?? error ?? 'no answer'}`,\n\t\t);\n\t\tsuper(\n\t\t\t`The \"${stage}\" stage was deployed, but ${surfaces.length === 1 ? 'a surface does' : 'surfaces do'} not answer healthy:\\n${lines.join('\\n')}\\nCheck its logs in CloudWatch, fix it, and deploy again.`,\n\t\t);\n\t\tthis.name = 'SurfacesUnhealthy';\n\t}\n}\n\n/** `path` under `base`, keeping any path `base` already has. */\nexport function healthUrl(base: string, path: string): string {\n\tconst root = base.endsWith('/') ? base : `${base}/`;\n\treturn new URL(path.replace(/^\\//, ''), root).toString();\n}\n\n/**\n * Ask `url` until it answers 2xx or the attempts run out, emitting\n * `health.checked` for each. Resolves with the last answer.\n */\nexport async function checkHealth(\n\tapp: string,\n\turl: string,\n\toptions: HealthCheckOptions,\n): Promise<{ healthy: boolean; status?: number; error?: string }> {\n\tlet last: { healthy: boolean; status?: number; error?: string } = {\n\t\thealthy: false,\n\t};\n\tfor (let attempt = 1; attempt <= options.attempts; attempt++) {\n\t\toptions.signal.throwIfAborted();\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tredirect: 'follow',\n\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\toptions.signal,\n\t\t\t\t\tAbortSignal.timeout(options.timeoutMs),\n\t\t\t\t]),\n\t\t\t});\n\t\t\t// Drained, so the connection is not held open by an unread body.\n\t\t\tawait response.arrayBuffer().catch(() => undefined);\n\t\t\tlast = { healthy: response.ok, status: response.status };\n\t\t} catch (error) {\n\t\t\t// The run being stopped is not the surface being down.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tlast = {\n\t\t\t\thealthy: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t\toptions.emit({\n\t\t\ttype: 'health.checked',\n\t\t\tapp,\n\t\t\turl,\n\t\t\thealthy: last.healthy,\n\t\t\t...(last.status !== undefined ? { status: last.status } : {}),\n\t\t\tattempt,\n\t\t});\n\t\tif (last.healthy) return last;\n\t\tif (attempt < options.attempts) await wait(options.delayMs, options.signal);\n\t}\n\treturn last;\n}\n\nfunction wait(ms: number, signal: AbortSignal): Promise<void> {\n\tif (ms <= 0) return Promise.resolve();\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal.removeEventListener('abort', stop);\n\t\t\tresolve();\n\t\t}, ms);\n\t\tconst stop = () => {\n\t\t\tclearTimeout(timer);\n\t\t\treject(signal.reason);\n\t\t};\n\t\tsignal.addEventListener('abort', stop, { once: true });\n\t});\n}\n","/**\n * The surface URLs an `sst deploy` reports.\n *\n * SST writes whatever `run()` in `sst.config.ts` returns to\n * `.sst/outputs.json` after every successful deploy — `{\"Api\": \"https://…\"}`.\n * What `run()` returns is the project's to write, so this reads it leniently:\n * nested objects are walked, anything that is not an http(s) URL is ignored,\n * and an output names a surface by its id in any casing, with or without a\n * `Url` suffix (`Api`, `api`, `ApiUrl`, `API_URL`, `Api.url`).\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { kebabCase } from '@geekmidas/manifest';\n\n/** Where SST leaves the last successful deploy's outputs, under the project. */\nexport const SST_OUTPUTS_FILE = join('.sst', 'outputs.json');\n\n/** `.sst/outputs.json` is there, and is not JSON. */\nexport class SstOutputsUnreadable extends Error {\n\tconstructor(\n\t\treadonly path: string,\n\t\treadonly reason: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not read SST's outputs at ${path}: ${reason}. It is rewritten by every successful \\`sst deploy\\`; delete it and deploy again.`,\n\t\t);\n\t\tthis.name = 'SstOutputsUnreadable';\n\t}\n}\n\n/**\n * Remove what an earlier deploy wrote, so the outputs read after this one\n * are this one's — not another stage's, deployed from the same checkout.\n */\nexport async function clearOutputs(cwd: string): Promise<void> {\n\tawait rm(join(cwd, SST_OUTPUTS_FILE), { force: true });\n}\n\n/** The deploy's outputs, or `{}` when it wrote none. */\nexport async function readOutputs(cwd: string): Promise<unknown> {\n\tconst path = join(cwd, SST_OUTPUTS_FILE);\n\tif (!existsSync(path)) return {};\n\ttry {\n\t\treturn JSON.parse(await readFile(path, 'utf8'));\n\t} catch (error) {\n\t\tthrow new SstOutputsUnreadable(\n\t\t\tpath,\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t);\n\t}\n}\n\n/** Every http(s) URL in `outputs`, by its key path: `['Api', 'url']`. */\nfunction urlsIn(\n\tvalue: unknown,\n\tpath: string[] = [],\n\tfound: { path: string[]; url: string }[] = [],\n): { path: string[]; url: string }[] {\n\tif (typeof value === 'string') {\n\t\tif (isHttpUrl(value)) found.push({ path, url: value });\n\t} else if (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\tfor (const [key, child] of Object.entries(value)) {\n\t\t\turlsIn(child, [...path, key], found);\n\t\t}\n\t}\n\treturn found;\n}\n\nfunction isHttpUrl(value: string): boolean {\n\ttry {\n\t\tconst { protocol } = new URL(value);\n\t\treturn protocol === 'http:' || protocol === 'https:';\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * The app an output's key path names, kebab-cased as app keys are: its last\n * segment, without a `url` suffix — or its parent's, when the last segment\n * is only `url`. Exact names are tried first, so an app called `api-url`\n * still finds `ApiUrl`.\n */\nfunction namesOf(path: string[]): string[] {\n\tconst names: string[] = [];\n\tfor (const segment of [...path].reverse()) {\n\t\tconst name = kebabCase(segment);\n\t\tnames.push(name);\n\t\tconst bare = name.replace(/-?url$/, '');\n\t\tif (bare) {\n\t\t\tnames.push(bare);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn names;\n}\n\n/**\n * Each app's URL from the outputs, for the apps given. An app no output\n * names is left out.\n */\nexport function surfaceUrls(\n\toutputs: unknown,\n\tapps: readonly string[],\n): Record<string, string> {\n\tconst found = urlsIn(outputs);\n\tconst urls: Record<string, string> = {};\n\tfor (const app of apps) {\n\t\t// An exact name beats a stripped one, wherever each is in the file.\n\t\tconst exact = found.find(({ path }) => namesOf(path)[0] === app);\n\t\tconst loose = found.find(({ path }) => namesOf(path).includes(app));\n\t\tconst match = exact ?? loose;\n\t\tif (match) urls[app] = match.url;\n\t}\n\treturn urls;\n}\n","/**\n * The built-in `sst` target: `gkm build` writes the manifest, `sst deploy`\n * provisions it, and the surfaces SST reports are health-checked.\n *\n * `@geekmidas/cloud` is unchanged by it: `sst.config.ts` still reads\n * `.gkm/manifest/aws.ts` and `fromManifest` still makes every resource. What\n * the target adds is the run around the two commands — the lock, the events,\n * verification, and credentials kept where they belong.\n *\n * Both commands run in the sandbox: the build executes the project's code,\n * and so does `sst deploy`, which evaluates `sst.config.ts`. Only the second\n * needs AWS, so the AWS credential goes into that one command's environment\n * and nowhere else — not the build's, not the process the deploy runs in.\n * What `sst deploy` needs beyond them, the sandbox already passes: `PATH`\n * for node and the package manager, and `HOME`, under which SST keeps its\n * toolchain (`~/.config/sst`: bun, pulumi and its plugins) and the AWS SDK\n * finds `~/.aws` for a profile. Its state — the Pulumi backend and the\n * passphrase — is in the stage's own account, reached with that credential.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport {\n\ttype AwsCredential,\n\tMissingCredential,\n} from '../../deploy/credentials';\nimport type { DeployResult } from '../../deploy/types';\nimport { detectPackageManager, type PackageManager } from '../../init/utils';\nimport { CommandFailed } from '../../run';\nimport { LocalSandbox } from '../../sandbox/local';\nimport { activeSandbox, type Sandbox } from '../../sandbox/sandbox';\nimport { appKey } from '../../workspace/derive';\nimport { defineTarget } from '../define';\nimport type { DeployPhaseContext, DeployTarget } from '../types';\nimport { checkHealth, healthUrl, SurfacesUnhealthy } from './health';\nimport { clearOutputs, readOutputs, surfaceUrls } from './outputs';\n\nexport { SurfacesUnhealthy } from './health';\nexport { SST_OUTPUTS_FILE, SstOutputsUnreadable } from './outputs';\n\n/** A cold build of every handler and site. */\nconst BUILD_TIMEOUT_MS = 30 * 60_000;\n/**\n * A first deploy creates CloudFront distributions and, with a database, a\n * VPC and an RDS instance — each of which alone can take a quarter of an hour.\n */\nconst DEPLOY_TIMEOUT_MS = 90 * 60_000;\n\n/** The workspace has no `sst.config.ts` for `sst deploy` to run. */\nexport class SstConfigNotFound extends Error {\n\tconstructor(readonly cwd: string) {\n\t\tsuper(\n\t\t\t`No sst.config.ts in ${cwd}. The sst target runs \\`sst deploy\\` at the workspace root: scaffold one with \\`gkm init --deploy sst\\` and copy its sst.config.ts, or deploy through another target with --target.`,\n\t\t);\n\t\tthis.name = 'SstConfigNotFound';\n\t}\n}\n\n/** What the phases hand on. */\nexport interface SstRun {\n\tsandbox: Sandbox;\n\tpm: PackageManager;\n\taws: AwsCredential;\n\t/** The apps SST deploys this run: `ctx.apps` without the skipped. */\n\tapps: string[];\n\t/** Each app's URL, from the deploy's outputs. */\n\turls: Record<string, string>;\n}\n\nexport interface SstTargetOptions {\n\t/** How often, and how patiently, `verify` asks each surface. */\n\thealth?: { attempts?: number; delayMs?: number; timeoutMs?: number };\n}\n\n/**\n * The project's own copy of `bin`, through its package manager — never one\n * `npx` would download, which would be code nobody installed.\n */\nexport function projectCommand(\n\tpm: PackageManager,\n\tbin: string,\n\targs: readonly string[],\n): [command: string, args: string[]] {\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn ['pnpm', ['exec', bin, ...args]];\n\t\tcase 'yarn':\n\t\t\treturn ['yarn', [bin, ...args]];\n\t\tcase 'bun':\n\t\t\treturn ['bunx', [bin, ...args]];\n\t\tcase 'npm':\n\t\t\treturn ['npx', ['--no-install', bin, ...args]];\n\t}\n}\n\n/**\n * The variables `credential` is to the AWS SDK and to SST.\n *\n * A profile is passed alone: given keys too, every SDK would take the keys.\n */\nexport function awsEnv(credential: AwsCredential): Record<string, string> {\n\tconst region: Record<string, string> = credential.region\n\t\t? { AWS_REGION: credential.region, AWS_DEFAULT_REGION: credential.region }\n\t\t: {};\n\tif ('profile' in credential) {\n\t\treturn { AWS_PROFILE: credential.profile, ...region };\n\t}\n\treturn {\n\t\tAWS_ACCESS_KEY_ID: credential.accessKeyId,\n\t\tAWS_SECRET_ACCESS_KEY: credential.secretAccessKey,\n\t\t...(credential.sessionToken\n\t\t\t? { AWS_SESSION_TOKEN: credential.sessionToken }\n\t\t\t: {}),\n\t\t...region,\n\t};\n}\n\n/**\n * `env` without any `AWS_*`, so a host sandbox that passes some cannot put\n * a second account beside the one the credential names.\n */\nfunction withoutAws(\n\tenv: Readonly<Record<string, string>>,\n): Record<string, string> {\n\treturn Object.fromEntries(\n\t\tObject.entries(env).filter(([name]) => !name.startsWith('AWS_')),\n\t);\n}\n\n/** Each app's kind in the manifest, by app key: `rest-api` or `site`. */\nfunction kindsOf(manifest: ConstructManifest): Map<string, string> {\n\treturn new Map(\n\t\tObject.entries(manifest).map(([id, declaration]) => [\n\t\t\tappKey(id),\n\t\t\tdeclaration.kind,\n\t\t]),\n\t);\n}\n\nasync function exec(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n\t[command, args]: [string, string[]],\n\toptions: { env: Record<string, string>; timeoutMs: number },\n): Promise<void> {\n\tconst result = await run.sandbox.exec(command, args, {\n\t\tcwd: ctx.cwd,\n\t\tenv: options.env,\n\t\ttimeoutMs: options.timeoutMs,\n\t\toutput: ctx.childOutput,\n\t\tsignal: ctx.signal,\n\t});\n\tif (result.exitCode !== 0) {\n\t\tthrow new CommandFailed(\n\t\t\tcommand,\n\t\t\targs,\n\t\t\tresult.exitCode,\n\t\t\tresult.signal as NodeJS.Signals | null,\n\t\t);\n\t}\n}\n\n/** The `sst` target, with its health checks tuned — for tests and hosts. */\nexport function createSstTarget(\n\toptions: SstTargetOptions = {},\n): DeployTarget<undefined, SstRun> {\n\tconst health = {\n\t\tattempts: options.health?.attempts ?? 6,\n\t\tdelayMs: options.health?.delayMs ?? 5_000,\n\t\ttimeoutMs: options.health?.timeoutMs ?? 10_000,\n\t};\n\n\treturn defineTarget<undefined, SstRun>({\n\t\tname: 'sst',\n\t\truntime: 'aws',\n\t\t// SST keeps no previous release to return to; migrations run in the\n\t\t// stack, as `fromManifest` provisions the database; and nothing here\n\t\t// builds an image.\n\t\tcapabilities: { rollback: false, migrations: 'target', images: false },\n\t\tcredentials: ['aws'],\n\n\t\tasync validate(ctx) {\n\t\t\tif (!existsSync(join(ctx.cwd, 'sst.config.ts'))) {\n\t\t\t\tthrow new SstConfigNotFound(ctx.cwd);\n\t\t\t}\n\n\t\t\tconst aws = await ctx.credentials.get(\n\t\t\t\t{ kind: 'aws', stage: ctx.stage },\n\t\t\t\t{ signal: ctx.signal },\n\t\t\t);\n\t\t\tif (!aws) throw new MissingCredential('aws', ctx.stage);\n\t\t\tif (!('profile' in aws)) {\n\t\t\t\tctx.secrets.mask(aws.secretAccessKey);\n\t\t\t\tif (aws.sessionToken) ctx.secrets.mask(aws.sessionToken);\n\t\t\t}\n\n\t\t\t// A mobile app ships through its own store's toolchain.\n\t\t\tfor (const app of ctx.apps) {\n\t\t\t\tif (ctx.workspace.apps[app]?.type === 'mobile') {\n\t\t\t\t\tctx.skip(app, 'mobile apps deploy through their own toolchain');\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst skipped = new Set(ctx.skipped.map(({ app }) => app));\n\t\t\tconst apps = ctx.apps.filter((app) => !skipped.has(app));\n\n\t\t\tctx.logger.info(\n\t\t\t\t`\\n🚀 Deploying \"${ctx.workspace.name}\" to the \"${ctx.stage}\" stage with SST`,\n\t\t\t);\n\t\t\tctx.logger.info(\n\t\t\t\t` AWS: ${'profile' in aws ? `profile \"${aws.profile}\"` : 'access keys'}`,\n\t\t\t);\n\t\t\t// `sst deploy` takes the whole stack: there is no deploying one app.\n\t\t\tconst deployable = Object.values(ctx.workspace.apps).filter(\n\t\t\t\t(app) => app.type !== 'mobile',\n\t\t\t).length;\n\t\t\tif (apps.length < deployable) {\n\t\t\t\tctx.logger.warn(\n\t\t\t\t\t` sst deploy deploys every construct of the stage, not only ${apps.join(', ')}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsandbox: activeSandbox() ?? new LocalSandbox({ root: ctx.cwd }),\n\t\t\t\tpm: detectPackageManager(ctx.cwd),\n\t\t\t\taws,\n\t\t\t\tapps,\n\t\t\t\turls: {},\n\t\t\t};\n\t\t},\n\n\t\tasync plan(ctx, run) {\n\t\t\tconst kinds = kindsOf(ctx.manifest);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'resource.planned',\n\t\t\t\t\tkey: `application:${app}`,\n\t\t\t\t\tresourceType: kinds.get(app) ?? 'application',\n\t\t\t\t\taction: 'ensure',\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst [build, buildArgs] = buildCommand(ctx, run);\n\t\t\tconst [deploy, deployArgs] = deployCommand(ctx, run);\n\t\t\tctx.logger.info(` Would run: ${[build, ...buildArgs].join(' ')}`);\n\t\t\tctx.logger.info(` Would run: ${[deploy, ...deployArgs].join(' ')}`);\n\t\t},\n\n\t\tasync build(ctx, run) {\n\t\t\tctx.logger.info('\\n📦 Building the manifest SST deploys');\n\t\t\t// No credential: the build runs the project's code and needs none.\n\t\t\tawait exec(ctx, run, buildCommand(ctx, run), {\n\t\t\t\tenv: { ...run.sandbox.env },\n\t\t\t\ttimeoutMs: BUILD_TIMEOUT_MS,\n\t\t\t});\n\t\t},\n\n\t\tasync release(ctx, run) {\n\t\t\tctx.logger.info(`\\n☁️ sst deploy --stage ${ctx.stage}`);\n\t\t\tawait clearOutputs(ctx.cwd);\n\t\t\tawait exec(ctx, run, deployCommand(ctx, run), {\n\t\t\t\tenv: { ...withoutAws(run.sandbox.env), ...awsEnv(run.aws) },\n\t\t\t\ttimeoutMs: DEPLOY_TIMEOUT_MS,\n\t\t\t});\n\n\t\t\trun.urls = surfaceUrls(await readOutputs(ctx.cwd), run.apps);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'app.deployed',\n\t\t\t\t\tapp,\n\t\t\t\t\tapplicationId: ctx.name(app),\n\t\t\t\t\t// SST builds no image a registry holds.\n\t\t\t\t\timageRef: '',\n\t\t\t\t\turl: run.urls[app] ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\n\t\tasync verify(ctx, run) {\n\t\t\tconst unhealthy: SurfacesUnhealthy['surfaces'][number][] = [];\n\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tconst base = run.urls[app];\n\t\t\t\tif (!base) {\n\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t` ⚠ No URL for ${app} in SST's outputs, so it was not checked. Return it from run() in sst.config.ts — { ${app}: <its url> } — to have it checked.`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// A surface — a `rest-api` — is the backend app it derives.\n\t\t\t\tconst backend = ctx.workspace.apps[app]?.type === 'backend';\n\t\t\t\tconst url = healthUrl(base, backend ? '/health' : '/');\n\t\t\t\tconst answer = await checkHealth(app, url, {\n\t\t\t\t\t...health,\n\t\t\t\t\tsignal: ctx.signal,\n\t\t\t\t\temit: ctx.emit,\n\t\t\t\t});\n\t\t\t\tif (answer.healthy) {\n\t\t\t\t\tctx.logger.info(` ✓ ${app}: ${url}`);\n\t\t\t\t} else {\n\t\t\t\t\tunhealthy.push({\n\t\t\t\t\t\tapp,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\t...(answer.status !== undefined ? { status: answer.status } : {}),\n\t\t\t\t\t\t...(answer.error ? { error: answer.error } : {}),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (unhealthy.length > 0) {\n\t\t\t\tthrow new SurfacesUnhealthy(ctx.stage, unhealthy);\n\t\t\t}\n\t\t},\n\n\t\tresult(ctx, run): DeployResult {\n\t\t\treturn {\n\t\t\t\tapps: run.apps.map((appName) => ({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: ctx.workspace.apps[appName]?.type ?? 'backend',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\t...(run.urls[appName] ? { url: run.urls[appName] } : {}),\n\t\t\t\t})),\n\t\t\t\tprojectId: '',\n\t\t\t\tenvironmentId: '',\n\t\t\t\tsuccessCount: ctx.dryRun ? 0 : run.apps.length,\n\t\t\t\tfailedCount: 0,\n\t\t\t\tstage: ctx.stage,\n\t\t\t\tidentity: ctx.identity.key,\n\t\t\t\ttag: ctx.tag,\n\t\t\t\tdryRun: ctx.dryRun,\n\t\t\t\tskipped: [...ctx.skipped],\n\t\t\t\turls: { ...run.urls },\n\t\t\t\tchanges: [],\n\t\t\t};\n\t\t},\n\t});\n}\n\nfunction buildCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'gkm', [\n\t\t'build',\n\t\t'--provider',\n\t\t'aws',\n\t\t'--stage',\n\t\tctx.stage,\n\t]);\n}\n\nfunction deployCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'sst', ['deploy', '--stage', ctx.stage]);\n}\n\nexport const sstTarget = createSstTarget();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sst-CJzvyrEz.mjs","names":[],"sources":["../src/target/sst/health.ts","../src/target/sst/outputs.ts","../src/target/sst/index.ts"],"sourcesContent":["/**\n * Whether a released surface answers.\n *\n * The paths are the ones the local stack's healthchecks use\n * (`reconcile/apps.ts`): an API's `/health`, a site's `/`. A surface is\n * asked a few times, because a fresh deploy can take a moment to answer — a\n * Lambda's first start, a distribution still settling — and healthy means a\n * 2xx, after redirects.\n */\n\nimport type { TargetEvent } from '../types';\n\nexport interface HealthCheckOptions {\n\t/** How many times a surface is asked before it counts as down. */\n\tattempts: number;\n\t/** The wait between two attempts. */\n\tdelayMs: number;\n\t/** How long one request may take. */\n\ttimeoutMs: number;\n\tsignal: AbortSignal;\n\temit: (event: TargetEvent) => void;\n}\n\n/** Surfaces that never answered healthy. */\nexport class SurfacesUnhealthy extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\t/** Each surface that failed, with the last thing it answered. */\n\t\treadonly surfaces: readonly {\n\t\t\tapp: string;\n\t\t\turl: string;\n\t\t\tstatus?: number;\n\t\t\terror?: string;\n\t\t}[],\n\t) {\n\t\tconst lines = surfaces.map(\n\t\t\t({ app, url, status, error }) =>\n\t\t\t\t` - ${app}: ${url} → ${status ?? error ?? 'no answer'}`,\n\t\t);\n\t\tsuper(\n\t\t\t`The \"${stage}\" stage was deployed, but ${surfaces.length === 1 ? 'a surface does' : 'surfaces do'} not answer healthy:\\n${lines.join('\\n')}\\nCheck its logs in CloudWatch, fix it, and deploy again.`,\n\t\t);\n\t\tthis.name = 'SurfacesUnhealthy';\n\t}\n}\n\n/** `path` under `base`, keeping any path `base` already has. */\nexport function healthUrl(base: string, path: string): string {\n\tconst root = base.endsWith('/') ? base : `${base}/`;\n\treturn new URL(path.replace(/^\\//, ''), root).toString();\n}\n\n/**\n * Ask `url` until it answers 2xx or the attempts run out, emitting\n * `health.checked` for each. Resolves with the last answer.\n */\nexport async function checkHealth(\n\tapp: string,\n\turl: string,\n\toptions: HealthCheckOptions,\n): Promise<{ healthy: boolean; status?: number; error?: string }> {\n\tlet last: { healthy: boolean; status?: number; error?: string } = {\n\t\thealthy: false,\n\t};\n\tfor (let attempt = 1; attempt <= options.attempts; attempt++) {\n\t\toptions.signal.throwIfAborted();\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tredirect: 'follow',\n\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\toptions.signal,\n\t\t\t\t\tAbortSignal.timeout(options.timeoutMs),\n\t\t\t\t]),\n\t\t\t});\n\t\t\t// Drained, so the connection is not held open by an unread body.\n\t\t\tawait response.arrayBuffer().catch(() => undefined);\n\t\t\tlast = { healthy: response.ok, status: response.status };\n\t\t} catch (error) {\n\t\t\t// The run being stopped is not the surface being down.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tlast = {\n\t\t\t\thealthy: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t\toptions.emit({\n\t\t\ttype: 'health.checked',\n\t\t\tapp,\n\t\t\turl,\n\t\t\thealthy: last.healthy,\n\t\t\t...(last.status !== undefined ? { status: last.status } : {}),\n\t\t\tattempt,\n\t\t});\n\t\tif (last.healthy) return last;\n\t\tif (attempt < options.attempts) await wait(options.delayMs, options.signal);\n\t}\n\treturn last;\n}\n\nfunction wait(ms: number, signal: AbortSignal): Promise<void> {\n\tif (ms <= 0) return Promise.resolve();\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal.removeEventListener('abort', stop);\n\t\t\tresolve();\n\t\t}, ms);\n\t\tconst stop = () => {\n\t\t\tclearTimeout(timer);\n\t\t\treject(signal.reason);\n\t\t};\n\t\tsignal.addEventListener('abort', stop, { once: true });\n\t});\n}\n","/**\n * The surface URLs an `sst deploy` reports.\n *\n * SST writes whatever `run()` in `sst.config.ts` returns to\n * `.sst/outputs.json` after every successful deploy — `{\"Api\": \"https://…\"}`.\n * What `run()` returns is the project's to write, so this reads it leniently:\n * nested objects are walked, anything that is not an http(s) URL is ignored,\n * and an output names a surface by its id in any casing, with or without a\n * `Url` suffix (`Api`, `api`, `ApiUrl`, `API_URL`, `Api.url`).\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { kebabCase } from '@geekmidas/manifest';\n\n/** Where SST leaves the last successful deploy's outputs, under the project. */\nexport const SST_OUTPUTS_FILE = join('.sst', 'outputs.json');\n\n/** `.sst/outputs.json` is there, and is not JSON. */\nexport class SstOutputsUnreadable extends Error {\n\tconstructor(\n\t\treadonly path: string,\n\t\treadonly reason: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not read SST's outputs at ${path}: ${reason}. It is rewritten by every successful \\`sst deploy\\`; delete it and deploy again.`,\n\t\t);\n\t\tthis.name = 'SstOutputsUnreadable';\n\t}\n}\n\n/**\n * Remove what an earlier deploy wrote, so the outputs read after this one\n * are this one's — not another stage's, deployed from the same checkout.\n */\nexport async function clearOutputs(cwd: string): Promise<void> {\n\tawait rm(join(cwd, SST_OUTPUTS_FILE), { force: true });\n}\n\n/** The deploy's outputs, or `{}` when it wrote none. */\nexport async function readOutputs(cwd: string): Promise<unknown> {\n\tconst path = join(cwd, SST_OUTPUTS_FILE);\n\tif (!existsSync(path)) return {};\n\ttry {\n\t\treturn JSON.parse(await readFile(path, 'utf8'));\n\t} catch (error) {\n\t\tthrow new SstOutputsUnreadable(\n\t\t\tpath,\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t);\n\t}\n}\n\n/** Every http(s) URL in `outputs`, by its key path: `['Api', 'url']`. */\nfunction urlsIn(\n\tvalue: unknown,\n\tpath: string[] = [],\n\tfound: { path: string[]; url: string }[] = [],\n): { path: string[]; url: string }[] {\n\tif (typeof value === 'string') {\n\t\tif (isHttpUrl(value)) found.push({ path, url: value });\n\t} else if (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\tfor (const [key, child] of Object.entries(value)) {\n\t\t\turlsIn(child, [...path, key], found);\n\t\t}\n\t}\n\treturn found;\n}\n\nfunction isHttpUrl(value: string): boolean {\n\ttry {\n\t\tconst { protocol } = new URL(value);\n\t\treturn protocol === 'http:' || protocol === 'https:';\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * The app an output's key path names, kebab-cased as app keys are: its last\n * segment, without a `url` suffix — or its parent's, when the last segment\n * is only `url`. Exact names are tried first, so an app called `api-url`\n * still finds `ApiUrl`.\n */\nfunction namesOf(path: string[]): string[] {\n\tconst names: string[] = [];\n\tfor (const segment of [...path].reverse()) {\n\t\tconst name = kebabCase(segment);\n\t\tnames.push(name);\n\t\tconst bare = name.replace(/-?url$/, '');\n\t\tif (bare) {\n\t\t\tnames.push(bare);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn names;\n}\n\n/**\n * Each app's URL from the outputs, for the apps given. An app no output\n * names is left out.\n */\nexport function surfaceUrls(\n\toutputs: unknown,\n\tapps: readonly string[],\n): Record<string, string> {\n\tconst found = urlsIn(outputs);\n\tconst urls: Record<string, string> = {};\n\tfor (const app of apps) {\n\t\t// An exact name beats a stripped one, wherever each is in the file.\n\t\tconst exact = found.find(({ path }) => namesOf(path)[0] === app);\n\t\tconst loose = found.find(({ path }) => namesOf(path).includes(app));\n\t\tconst match = exact ?? loose;\n\t\tif (match) urls[app] = match.url;\n\t}\n\treturn urls;\n}\n","/**\n * The built-in `sst` target: `gkm build` writes the manifest, `sst deploy`\n * provisions it, and the surfaces SST reports are health-checked.\n *\n * `@geekmidas/cloud` is unchanged by it: `sst.config.ts` still reads\n * `.gkm/manifest/aws.ts` and `fromManifest` still makes every resource. What\n * the target adds is the run around the two commands — the lock, the events,\n * verification, and credentials kept where they belong.\n *\n * Both commands run in the sandbox: the build executes the project's code,\n * and so does `sst deploy`, which evaluates `sst.config.ts`. Only the second\n * needs AWS, so the AWS credential goes into that one command's environment\n * and nowhere else — not the build's, not the process the deploy runs in.\n * What `sst deploy` needs beyond them, the sandbox already passes: `PATH`\n * for node and the package manager, and `HOME`, under which SST keeps its\n * toolchain (`~/.config/sst`: bun, pulumi and its plugins) and the AWS SDK\n * finds `~/.aws` for a profile. Its state — the Pulumi backend and the\n * passphrase — is in the stage's own account, reached with that credential.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport {\n\ttype AwsCredential,\n\tMissingCredential,\n} from '../../deploy/credentials';\nimport type { DeployResult } from '../../deploy/types';\nimport { detectPackageManager, type PackageManager } from '../../init/utils';\nimport { CommandFailed } from '../../run';\nimport { LocalSandbox } from '../../sandbox/local';\nimport { activeSandbox, type Sandbox } from '../../sandbox/sandbox';\nimport { appKey } from '../../workspace/derive';\nimport { defineTarget } from '../define';\nimport type { DeployPhaseContext, DeployTarget } from '../types';\nimport { checkHealth, healthUrl, SurfacesUnhealthy } from './health';\nimport { clearOutputs, readOutputs, surfaceUrls } from './outputs';\n\nexport { SurfacesUnhealthy } from './health';\nexport { SST_OUTPUTS_FILE, SstOutputsUnreadable } from './outputs';\n\n/** A cold build of every handler and site. */\nconst BUILD_TIMEOUT_MS = 30 * 60_000;\n/**\n * A first deploy creates CloudFront distributions and, with a database, a\n * VPC and an RDS instance — each of which alone can take a quarter of an hour.\n */\nconst DEPLOY_TIMEOUT_MS = 90 * 60_000;\n\n/** The workspace has no `sst.config.ts` for `sst deploy` to run. */\nexport class SstConfigNotFound extends Error {\n\tconstructor(readonly cwd: string) {\n\t\tsuper(\n\t\t\t`No sst.config.ts in ${cwd}. The sst target runs \\`sst deploy\\` at the workspace root: scaffold one with \\`gkm init --deploy sst\\` and copy its sst.config.ts, or deploy through another target with --target.`,\n\t\t);\n\t\tthis.name = 'SstConfigNotFound';\n\t}\n}\n\n/** What the phases hand on. */\nexport interface SstRun {\n\tsandbox: Sandbox;\n\tpm: PackageManager;\n\taws: AwsCredential;\n\t/** The apps SST deploys this run: `ctx.apps` without the skipped. */\n\tapps: string[];\n\t/** Each app's URL, from the deploy's outputs. */\n\turls: Record<string, string>;\n}\n\nexport interface SstTargetOptions {\n\t/** How often, and how patiently, `verify` asks each surface. */\n\thealth?: { attempts?: number; delayMs?: number; timeoutMs?: number };\n}\n\n/**\n * The project's own copy of `bin`, through its package manager — never one\n * `npx` would download, which would be code nobody installed.\n */\nexport function projectCommand(\n\tpm: PackageManager,\n\tbin: string,\n\targs: readonly string[],\n): [command: string, args: string[]] {\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn ['pnpm', ['exec', bin, ...args]];\n\t\tcase 'yarn':\n\t\t\treturn ['yarn', [bin, ...args]];\n\t\tcase 'bun':\n\t\t\treturn ['bunx', [bin, ...args]];\n\t\tcase 'npm':\n\t\t\treturn ['npx', ['--no-install', bin, ...args]];\n\t}\n}\n\n/**\n * The variables `credential` is to the AWS SDK and to SST.\n *\n * A profile is passed alone: given keys too, every SDK would take the keys.\n */\nexport function awsEnv(credential: AwsCredential): Record<string, string> {\n\tconst region: Record<string, string> = credential.region\n\t\t? { AWS_REGION: credential.region, AWS_DEFAULT_REGION: credential.region }\n\t\t: {};\n\tif ('profile' in credential) {\n\t\treturn { AWS_PROFILE: credential.profile, ...region };\n\t}\n\treturn {\n\t\tAWS_ACCESS_KEY_ID: credential.accessKeyId,\n\t\tAWS_SECRET_ACCESS_KEY: credential.secretAccessKey,\n\t\t...(credential.sessionToken\n\t\t\t? { AWS_SESSION_TOKEN: credential.sessionToken }\n\t\t\t: {}),\n\t\t...region,\n\t};\n}\n\n/**\n * `env` without any `AWS_*`, so a host sandbox that passes some cannot put\n * a second account beside the one the credential names.\n */\nfunction withoutAws(\n\tenv: Readonly<Record<string, string>>,\n): Record<string, string> {\n\treturn Object.fromEntries(\n\t\tObject.entries(env).filter(([name]) => !name.startsWith('AWS_')),\n\t);\n}\n\n/** Each app's kind in the manifest, by app key: `rest-api` or `site`. */\nfunction kindsOf(manifest: ConstructManifest): Map<string, string> {\n\treturn new Map(\n\t\tObject.entries(manifest).map(([id, declaration]) => [\n\t\t\tappKey(id),\n\t\t\tdeclaration.kind,\n\t\t]),\n\t);\n}\n\nasync function exec(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n\t[command, args]: [string, string[]],\n\toptions: { env: Record<string, string>; timeoutMs: number },\n): Promise<void> {\n\tconst result = await run.sandbox.exec(command, args, {\n\t\tcwd: ctx.cwd,\n\t\tenv: options.env,\n\t\ttimeoutMs: options.timeoutMs,\n\t\toutput: ctx.childOutput,\n\t\tsignal: ctx.signal,\n\t});\n\tif (result.exitCode !== 0) {\n\t\tthrow new CommandFailed(\n\t\t\tcommand,\n\t\t\targs,\n\t\t\tresult.exitCode,\n\t\t\tresult.signal as NodeJS.Signals | null,\n\t\t);\n\t}\n}\n\n/** The `sst` target, with its health checks tuned — for tests and hosts. */\nexport function createSstTarget(\n\toptions: SstTargetOptions = {},\n): DeployTarget<undefined, SstRun> {\n\tconst health = {\n\t\tattempts: options.health?.attempts ?? 6,\n\t\tdelayMs: options.health?.delayMs ?? 5_000,\n\t\ttimeoutMs: options.health?.timeoutMs ?? 10_000,\n\t};\n\n\treturn defineTarget<undefined, SstRun>({\n\t\tname: 'sst',\n\t\truntime: 'aws',\n\t\t// SST keeps no previous release to return to; migrations run in the\n\t\t// stack, as `fromManifest` provisions the database; and nothing here\n\t\t// builds an image.\n\t\tcapabilities: { rollback: false, migrations: 'target', images: false },\n\t\tcredentials: ['aws'],\n\n\t\tasync validate(ctx) {\n\t\t\tif (!existsSync(join(ctx.cwd, 'sst.config.ts'))) {\n\t\t\t\tthrow new SstConfigNotFound(ctx.cwd);\n\t\t\t}\n\n\t\t\tconst aws = await ctx.credentials.get(\n\t\t\t\t{ kind: 'aws', stage: ctx.stage },\n\t\t\t\t{ signal: ctx.signal },\n\t\t\t);\n\t\t\tif (!aws) throw new MissingCredential('aws', ctx.stage);\n\t\t\tif (!('profile' in aws)) {\n\t\t\t\tctx.secrets.mask(aws.secretAccessKey);\n\t\t\t\tif (aws.sessionToken) ctx.secrets.mask(aws.sessionToken);\n\t\t\t}\n\n\t\t\t// A mobile app ships through its own store's toolchain.\n\t\t\tfor (const app of ctx.apps) {\n\t\t\t\tif (ctx.workspace.apps[app]?.type === 'mobile') {\n\t\t\t\t\tctx.skip(app, 'mobile apps deploy through their own toolchain');\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst skipped = new Set(ctx.skipped.map(({ app }) => app));\n\t\t\tconst apps = ctx.apps.filter((app) => !skipped.has(app));\n\n\t\t\tctx.logger.info(\n\t\t\t\t`\\n🚀 Deploying \"${ctx.workspace.name}\" to the \"${ctx.stage}\" stage with SST`,\n\t\t\t);\n\t\t\tctx.logger.info(\n\t\t\t\t` AWS: ${'profile' in aws ? `profile \"${aws.profile}\"` : 'access keys'}`,\n\t\t\t);\n\t\t\t// `sst deploy` takes the whole stack: there is no deploying one app.\n\t\t\tconst deployable = Object.values(ctx.workspace.apps).filter(\n\t\t\t\t(app) => app.type !== 'mobile',\n\t\t\t).length;\n\t\t\tif (apps.length < deployable) {\n\t\t\t\tctx.logger.warn(\n\t\t\t\t\t` sst deploy deploys every construct of the stage, not only ${apps.join(', ')}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsandbox: activeSandbox() ?? new LocalSandbox({ root: ctx.cwd }),\n\t\t\t\tpm: detectPackageManager(ctx.cwd),\n\t\t\t\taws,\n\t\t\t\tapps,\n\t\t\t\turls: {},\n\t\t\t};\n\t\t},\n\n\t\tasync plan(ctx, run) {\n\t\t\tconst kinds = kindsOf(ctx.manifest);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'resource.planned',\n\t\t\t\t\tkey: `application:${app}`,\n\t\t\t\t\tresourceType: kinds.get(app) ?? 'application',\n\t\t\t\t\taction: 'ensure',\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst [build, buildArgs] = buildCommand(ctx, run);\n\t\t\tconst [deploy, deployArgs] = deployCommand(ctx, run);\n\t\t\tctx.logger.info(` Would run: ${[build, ...buildArgs].join(' ')}`);\n\t\t\tctx.logger.info(` Would run: ${[deploy, ...deployArgs].join(' ')}`);\n\t\t},\n\n\t\tasync build(ctx, run) {\n\t\t\tctx.logger.info('\\n📦 Building the manifest SST deploys');\n\t\t\t// No credential: the build runs the project's code and needs none.\n\t\t\tawait exec(ctx, run, buildCommand(ctx, run), {\n\t\t\t\tenv: { ...run.sandbox.env },\n\t\t\t\ttimeoutMs: BUILD_TIMEOUT_MS,\n\t\t\t});\n\t\t},\n\n\t\tasync release(ctx, run) {\n\t\t\tctx.logger.info(`\\n☁️ sst deploy --stage ${ctx.stage}`);\n\t\t\tawait clearOutputs(ctx.cwd);\n\t\t\tawait exec(ctx, run, deployCommand(ctx, run), {\n\t\t\t\tenv: { ...withoutAws(run.sandbox.env), ...awsEnv(run.aws) },\n\t\t\t\ttimeoutMs: DEPLOY_TIMEOUT_MS,\n\t\t\t});\n\n\t\t\trun.urls = surfaceUrls(await readOutputs(ctx.cwd), run.apps);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'app.deployed',\n\t\t\t\t\tapp,\n\t\t\t\t\tapplicationId: ctx.name(app),\n\t\t\t\t\t// SST builds no image a registry holds.\n\t\t\t\t\timageRef: '',\n\t\t\t\t\turl: run.urls[app] ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\n\t\tasync verify(ctx, run) {\n\t\t\tconst unhealthy: SurfacesUnhealthy['surfaces'][number][] = [];\n\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tconst base = run.urls[app];\n\t\t\t\tif (!base) {\n\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t` ⚠ No URL for ${app} in SST's outputs, so it was not checked. Return it from run() in sst.config.ts — { ${app}: <its url> } — to have it checked.`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// A surface — a `rest-api` — is the backend app it derives.\n\t\t\t\tconst backend = ctx.workspace.apps[app]?.type === 'backend';\n\t\t\t\tconst url = healthUrl(base, backend ? '/health' : '/');\n\t\t\t\tconst answer = await checkHealth(app, url, {\n\t\t\t\t\t...health,\n\t\t\t\t\tsignal: ctx.signal,\n\t\t\t\t\temit: ctx.emit,\n\t\t\t\t});\n\t\t\t\tif (answer.healthy) {\n\t\t\t\t\tctx.logger.info(` ✓ ${app}: ${url}`);\n\t\t\t\t} else {\n\t\t\t\t\tunhealthy.push({\n\t\t\t\t\t\tapp,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\t...(answer.status !== undefined ? { status: answer.status } : {}),\n\t\t\t\t\t\t...(answer.error ? { error: answer.error } : {}),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (unhealthy.length > 0) {\n\t\t\t\tthrow new SurfacesUnhealthy(ctx.stage, unhealthy);\n\t\t\t}\n\t\t},\n\n\t\tresult(ctx, run): DeployResult {\n\t\t\treturn {\n\t\t\t\tapps: run.apps.map((appName) => ({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: ctx.workspace.apps[appName]?.type ?? 'backend',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\t...(run.urls[appName] ? { url: run.urls[appName] } : {}),\n\t\t\t\t})),\n\t\t\t\tprojectId: '',\n\t\t\t\tenvironmentId: '',\n\t\t\t\tsuccessCount: ctx.dryRun ? 0 : run.apps.length,\n\t\t\t\tfailedCount: 0,\n\t\t\t\tstage: ctx.stage,\n\t\t\t\tidentity: ctx.identity.key,\n\t\t\t\ttag: ctx.tag,\n\t\t\t\tdryRun: ctx.dryRun,\n\t\t\t\tskipped: [...ctx.skipped],\n\t\t\t\turls: { ...run.urls },\n\t\t\t\tchanges: [],\n\t\t\t};\n\t\t},\n\t});\n}\n\nfunction buildCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'gkm', [\n\t\t'build',\n\t\t'--provider',\n\t\t'aws',\n\t\t'--stage',\n\t\tctx.stage,\n\t]);\n}\n\nfunction deployCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'sst', ['deploy', '--stage', ctx.stage]);\n}\n\nexport const sstTarget = createSstTarget();\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_workspace = require("./workspace-
|
|
1
|
+
const require_workspace = require("./workspace-CwW3Gkru.cjs");
|
|
2
2
|
const require_config = require("./config.cjs");
|
|
3
3
|
let node_fs_promises = require("node:fs/promises");
|
|
4
4
|
//#region src/stages/plan.ts
|
|
@@ -171,4 +171,4 @@ var GithubEventNotSet = class extends Error {
|
|
|
171
171
|
//#endregion
|
|
172
172
|
exports.stagesCommand = stagesCommand;
|
|
173
173
|
|
|
174
|
-
//# sourceMappingURL=stages-
|
|
174
|
+
//# sourceMappingURL=stages-CUn6gvXL.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stages-Brn3vcdy.cjs","names":["UndeclaredStage","loadWorkspaceSettings","appendFile"],"sources":["../src/stages/plan.ts","../src/stages/index.ts"],"sourcesContent":["/**\n * Which stages a workflow run builds and deploys, from the event that started\n * it and the project's `stages`.\n *\n * The rules live here, in tested code, rather than in a workflow's YAML or in\n * the action that runs `gkm stages --github-output`: a project's deploy\n * workflow names no stage, and what it does for each event is decided by the\n * CLI the project installed.\n *\n * | event | build | deploy |\n * | ------------------- | ------------------ | ------------------------------- |\n * | `push` | every deployed | deployed, less the protected |\n * | `release` | none | the protected |\n * | `workflow_dispatch` | none | the one stage named |\n * | anything else | none | none |\n *\n * A push builds every deployed stage, protected ones included, so a release\n * deploys images built from the very commit its tag names; a release and a\n * manual run deploy what a push already built.\n */\n\nimport { UndeclaredStage } from '../workspace/stages.js';\nimport type { StagesConfig } from '../workspace/types.js';\n\nexport interface StagePlan {\n\t/** The stages whose images this run builds and pushes. */\n\tbuild: string[];\n\t/** The stages this run deploys. */\n\tdeploy: string[];\n}\n\nexport interface PlanInput {\n\t/** The GitHub event name: `push`, `release`, `workflow_dispatch`, … */\n\tevent: string;\n\t/** The stage a `workflow_dispatch` names. */\n\tstage?: string;\n}\n\nexport function planStages(stages: StagesConfig, input: PlanInput): StagePlan {\n\tconst kept = stages.protected ?? [];\n\n\tswitch (input.event) {\n\t\tcase 'push':\n\t\t\treturn {\n\t\t\t\tbuild: [...stages.deployed],\n\t\t\t\tdeploy: stages.deployed.filter((stage) => !kept.includes(stage)),\n\t\t\t};\n\t\tcase 'release':\n\t\t\treturn { build: [], deploy: [...kept] };\n\t\tcase 'workflow_dispatch': {\n\t\t\tconst stage = input.stage?.trim();\n\t\t\tif (!stage) throw new DispatchNamesNoStage(stages.deployed);\n\t\t\tif (!stages.deployed.includes(stage)) {\n\t\t\t\tthrow new UndeclaredStage(stage, stages.deployed);\n\t\t\t}\n\t\t\treturn { build: [], deploy: [stage] };\n\t\t}\n\t\tdefault:\n\t\t\treturn { build: [], deploy: [] };\n\t}\n}\n\n/**\n * A manual run with no stage. Refused rather than read as \"nothing to do\", so\n * a run started with the field left empty fails where it can be seen.\n */\nexport class DispatchNamesNoStage extends Error {\n\tconstructor(readonly deployed: readonly string[]) {\n\t\tsuper(\n\t\t\t`A manual run deploys one stage, and none was given. Run the workflow again with stage set to one of: ${deployed.join(', ') || 'nothing yet — gkm.config.ts deploys to no stage'}.`,\n\t\t);\n\t\tthis.name = 'DispatchNamesNoStage';\n\t}\n}\n","/**\n * `gkm stages` — the workspace's stages, as gkm.config.ts declares them.\n *\n * ```bash\n * gkm stages # a table\n * gkm stages --json # {\"local\":\"dev\",\"deployed\":[\"prod\"],\"protected\":[\"prod\"]}\n * gkm stages --github-output # a workflow run's build and deploy matrices\n * ```\n *\n * The config is read by the normal loader (and sandbox), so a workflow never\n * parses TypeScript to learn which stages exist. `--github-output` is what the\n * `geekmidas/toolbox/actions/stages` action runs: it writes the stages, and\n * which of them this run builds and deploys, to `$GITHUB_OUTPUT`.\n */\n\nimport { appendFile } from 'node:fs/promises';\nimport { loadWorkspaceSettings } from '../config.js';\nimport type { NormalizedWorkspace, StagesConfig } from '../workspace/types.js';\nimport { planStages } from './plan.js';\n\nexport { DispatchNamesNoStage, type PlanInput, planStages } from './plan.js';\n\nexport interface StagesOptions {\n\t/** Print the stages as JSON. */\n\tjson?: boolean;\n\t/** Write the outputs a deploy workflow reads to `$GITHUB_OUTPUT`. */\n\tgithubOutput?: boolean;\n\t/** The event to plan for; defaults to `GITHUB_EVENT_NAME`. */\n\tevent?: string;\n\t/** The stage a `workflow_dispatch` names. */\n\tstage?: string;\n}\n\nexport interface StagesDeps {\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\t/** Where lines go; stdout by default. */\n\tlog?: (line: string) => void;\n}\n\n/** The stages, as `gkm stages --json` prints them. */\nexport interface StagesJson {\n\tlocal: string;\n\tdeployed: string[];\n\tprotected: string[];\n}\n\nexport function stagesJson(stages: StagesConfig): StagesJson {\n\treturn {\n\t\tlocal: stages.local,\n\t\tdeployed: [...stages.deployed],\n\t\tprotected: [...(stages.protected ?? [])],\n\t};\n}\n\n/**\n * The region of the workspace's AWS secrets store — `ssm` or\n * `secrets-manager` — or `''` when its deployed stages keep their secrets\n * anywhere else. A job assumes the stage's AWS role only when this is set.\n */\nexport function awsSecretsRegion(workspace: NormalizedWorkspace): string {\n\tconst store = workspace.secrets?.store;\n\tif (typeof store !== 'object' || store === null) return '';\n\tif (store.provider === 'ssm' || store.provider === 'secrets-manager') {\n\t\treturn store.region;\n\t}\n\treturn '';\n}\n\n/**\n * Every output the stages action sets, each a string `fromJSON()` reads, for\n * one workflow run.\n */\nexport function githubOutputs(\n\tworkspace: NormalizedWorkspace,\n\tinput: { event: string; stage?: string },\n): Record<string, string> {\n\tconst stages = stagesJson(workspace.stages);\n\tconst plan = planStages(workspace.stages, input);\n\n\treturn {\n\t\tlocal: JSON.stringify(stages.local),\n\t\tdeployed: JSON.stringify(stages.deployed),\n\t\tprotected: JSON.stringify(stages.protected),\n\t\tbuild: JSON.stringify(plan.build),\n\t\tdeploy: JSON.stringify(plan.deploy),\n\t\t// A matrix over an empty list is an error on GitHub, so a job over one\n\t\t// is skipped on these instead.\n\t\t'has-build': String(plan.build.length > 0),\n\t\t'has-deploy': String(plan.deploy.length > 0),\n\t\t'aws-region': awsSecretsRegion(workspace),\n\t};\n}\n\n/** `name=value` lines, as `$GITHUB_OUTPUT` takes single-line values. */\nexport function formatGithubOutputs(outputs: Record<string, string>): string {\n\treturn Object.entries(outputs)\n\t\t.map(([name, value]) => `${name}=${value}\\n`)\n\t\t.join('');\n}\n\n/** A message as a workflow command's data: GitHub reads `%`, CR and LF escaped. */\nfunction workflowCommandData(message: string): string {\n\treturn message\n\t\t.replaceAll('%', '%25')\n\t\t.replaceAll('\\r', '%0D')\n\t\t.replaceAll('\\n', '%0A');\n}\n\nfunction table(stages: StagesJson): string {\n\tconst rows: [string, string][] = [\n\t\t['STAGE', 'KIND'],\n\t\t[stages.local, 'local'],\n\t\t...stages.deployed.map((name): [string, string] => [\n\t\t\tname,\n\t\t\tstages.protected.includes(name) ? 'deployed, protected' : 'deployed',\n\t\t]),\n\t];\n\tconst width = Math.max(...rows.map(([name]) => name.length));\n\treturn rows\n\t\t.map(([name, kind]) => `${name.padEnd(width)} ${kind}`)\n\t\t.join('\\n');\n}\n\nexport async function stagesCommand(\n\toptions: StagesOptions,\n\tdeps: StagesDeps = {},\n): Promise<void> {\n\tconst env = deps.env ?? process.env;\n\tconst log = deps.log ?? ((line: string) => console.log(line));\n\n\tif (!options.githubOutput) {\n\t\tconst workspace = await loadWorkspaceSettings(deps.cwd);\n\t\tconst stages = stagesJson(workspace.stages);\n\t\tlog(options.json ? JSON.stringify(stages) : table(stages));\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst file = env.GITHUB_OUTPUT;\n\t\tif (!file) throw new GithubOutputNotSet();\n\n\t\tconst workspace = await loadWorkspaceSettings(deps.cwd);\n\t\tconst event = options.event || env.GITHUB_EVENT_NAME;\n\t\tif (!event) throw new GithubEventNotSet();\n\n\t\tconst outputs = githubOutputs(workspace, {\n\t\t\tevent,\n\t\t\t...(options.stage ? { stage: options.stage } : {}),\n\t\t});\n\t\tawait appendFile(file, formatGithubOutputs(outputs));\n\n\t\tlog(`${event}: build ${outputs.build}, deploy ${outputs.deploy}`);\n\t} catch (error) {\n\t\t// Annotated on the run, where whoever started it looks first.\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog(`::error title=gkm stages::${workflowCommandData(message)}`);\n\t\tthrow error;\n\t}\n}\n\n/** `--github-output` outside a GitHub Actions step, which sets the file. */\nexport class GithubOutputNotSet extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm stages --github-output writes to the file GITHUB_OUTPUT names, and it is not set. Run it in a GitHub Actions step, or set GITHUB_OUTPUT to a file to write to.',\n\t\t);\n\t\tthis.name = 'GithubOutputNotSet';\n\t}\n}\n\n/** No `--event`, and no `GITHUB_EVENT_NAME` to take it from. */\nexport class GithubEventNotSet extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm stages --github-output plans for an event, and none was given. Pass --event (push, release, workflow_dispatch), or run it in a GitHub Actions step, which sets GITHUB_EVENT_NAME.',\n\t\t);\n\t\tthis.name = 'GithubEventNotSet';\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stages-CUn6gvXL.cjs","names":["UndeclaredStage","loadWorkspaceSettings","appendFile"],"sources":["../src/stages/plan.ts","../src/stages/index.ts"],"sourcesContent":["/**\n * Which stages a workflow run builds and deploys, from the event that started\n * it and the project's `stages`.\n *\n * The rules live here, in tested code, rather than in a workflow's YAML or in\n * the action that runs `gkm stages --github-output`: a project's deploy\n * workflow names no stage, and what it does for each event is decided by the\n * CLI the project installed.\n *\n * | event | build | deploy |\n * | ------------------- | ------------------ | ------------------------------- |\n * | `push` | every deployed | deployed, less the protected |\n * | `release` | none | the protected |\n * | `workflow_dispatch` | none | the one stage named |\n * | anything else | none | none |\n *\n * A push builds every deployed stage, protected ones included, so a release\n * deploys images built from the very commit its tag names; a release and a\n * manual run deploy what a push already built.\n */\n\nimport { UndeclaredStage } from '../workspace/stages.js';\nimport type { StagesConfig } from '../workspace/types.js';\n\nexport interface StagePlan {\n\t/** The stages whose images this run builds and pushes. */\n\tbuild: string[];\n\t/** The stages this run deploys. */\n\tdeploy: string[];\n}\n\nexport interface PlanInput {\n\t/** The GitHub event name: `push`, `release`, `workflow_dispatch`, … */\n\tevent: string;\n\t/** The stage a `workflow_dispatch` names. */\n\tstage?: string;\n}\n\nexport function planStages(stages: StagesConfig, input: PlanInput): StagePlan {\n\tconst kept = stages.protected ?? [];\n\n\tswitch (input.event) {\n\t\tcase 'push':\n\t\t\treturn {\n\t\t\t\tbuild: [...stages.deployed],\n\t\t\t\tdeploy: stages.deployed.filter((stage) => !kept.includes(stage)),\n\t\t\t};\n\t\tcase 'release':\n\t\t\treturn { build: [], deploy: [...kept] };\n\t\tcase 'workflow_dispatch': {\n\t\t\tconst stage = input.stage?.trim();\n\t\t\tif (!stage) throw new DispatchNamesNoStage(stages.deployed);\n\t\t\tif (!stages.deployed.includes(stage)) {\n\t\t\t\tthrow new UndeclaredStage(stage, stages.deployed);\n\t\t\t}\n\t\t\treturn { build: [], deploy: [stage] };\n\t\t}\n\t\tdefault:\n\t\t\treturn { build: [], deploy: [] };\n\t}\n}\n\n/**\n * A manual run with no stage. Refused rather than read as \"nothing to do\", so\n * a run started with the field left empty fails where it can be seen.\n */\nexport class DispatchNamesNoStage extends Error {\n\tconstructor(readonly deployed: readonly string[]) {\n\t\tsuper(\n\t\t\t`A manual run deploys one stage, and none was given. Run the workflow again with stage set to one of: ${deployed.join(', ') || 'nothing yet — gkm.config.ts deploys to no stage'}.`,\n\t\t);\n\t\tthis.name = 'DispatchNamesNoStage';\n\t}\n}\n","/**\n * `gkm stages` — the workspace's stages, as gkm.config.ts declares them.\n *\n * ```bash\n * gkm stages # a table\n * gkm stages --json # {\"local\":\"dev\",\"deployed\":[\"prod\"],\"protected\":[\"prod\"]}\n * gkm stages --github-output # a workflow run's build and deploy matrices\n * ```\n *\n * The config is read by the normal loader (and sandbox), so a workflow never\n * parses TypeScript to learn which stages exist. `--github-output` is what the\n * `geekmidas/toolbox/actions/stages` action runs: it writes the stages, and\n * which of them this run builds and deploys, to `$GITHUB_OUTPUT`.\n */\n\nimport { appendFile } from 'node:fs/promises';\nimport { loadWorkspaceSettings } from '../config.js';\nimport type { NormalizedWorkspace, StagesConfig } from '../workspace/types.js';\nimport { planStages } from './plan.js';\n\nexport { DispatchNamesNoStage, type PlanInput, planStages } from './plan.js';\n\nexport interface StagesOptions {\n\t/** Print the stages as JSON. */\n\tjson?: boolean;\n\t/** Write the outputs a deploy workflow reads to `$GITHUB_OUTPUT`. */\n\tgithubOutput?: boolean;\n\t/** The event to plan for; defaults to `GITHUB_EVENT_NAME`. */\n\tevent?: string;\n\t/** The stage a `workflow_dispatch` names. */\n\tstage?: string;\n}\n\nexport interface StagesDeps {\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\t/** Where lines go; stdout by default. */\n\tlog?: (line: string) => void;\n}\n\n/** The stages, as `gkm stages --json` prints them. */\nexport interface StagesJson {\n\tlocal: string;\n\tdeployed: string[];\n\tprotected: string[];\n}\n\nexport function stagesJson(stages: StagesConfig): StagesJson {\n\treturn {\n\t\tlocal: stages.local,\n\t\tdeployed: [...stages.deployed],\n\t\tprotected: [...(stages.protected ?? [])],\n\t};\n}\n\n/**\n * The region of the workspace's AWS secrets store — `ssm` or\n * `secrets-manager` — or `''` when its deployed stages keep their secrets\n * anywhere else. A job assumes the stage's AWS role only when this is set.\n */\nexport function awsSecretsRegion(workspace: NormalizedWorkspace): string {\n\tconst store = workspace.secrets?.store;\n\tif (typeof store !== 'object' || store === null) return '';\n\tif (store.provider === 'ssm' || store.provider === 'secrets-manager') {\n\t\treturn store.region;\n\t}\n\treturn '';\n}\n\n/**\n * Every output the stages action sets, each a string `fromJSON()` reads, for\n * one workflow run.\n */\nexport function githubOutputs(\n\tworkspace: NormalizedWorkspace,\n\tinput: { event: string; stage?: string },\n): Record<string, string> {\n\tconst stages = stagesJson(workspace.stages);\n\tconst plan = planStages(workspace.stages, input);\n\n\treturn {\n\t\tlocal: JSON.stringify(stages.local),\n\t\tdeployed: JSON.stringify(stages.deployed),\n\t\tprotected: JSON.stringify(stages.protected),\n\t\tbuild: JSON.stringify(plan.build),\n\t\tdeploy: JSON.stringify(plan.deploy),\n\t\t// A matrix over an empty list is an error on GitHub, so a job over one\n\t\t// is skipped on these instead.\n\t\t'has-build': String(plan.build.length > 0),\n\t\t'has-deploy': String(plan.deploy.length > 0),\n\t\t'aws-region': awsSecretsRegion(workspace),\n\t};\n}\n\n/** `name=value` lines, as `$GITHUB_OUTPUT` takes single-line values. */\nexport function formatGithubOutputs(outputs: Record<string, string>): string {\n\treturn Object.entries(outputs)\n\t\t.map(([name, value]) => `${name}=${value}\\n`)\n\t\t.join('');\n}\n\n/** A message as a workflow command's data: GitHub reads `%`, CR and LF escaped. */\nfunction workflowCommandData(message: string): string {\n\treturn message\n\t\t.replaceAll('%', '%25')\n\t\t.replaceAll('\\r', '%0D')\n\t\t.replaceAll('\\n', '%0A');\n}\n\nfunction table(stages: StagesJson): string {\n\tconst rows: [string, string][] = [\n\t\t['STAGE', 'KIND'],\n\t\t[stages.local, 'local'],\n\t\t...stages.deployed.map((name): [string, string] => [\n\t\t\tname,\n\t\t\tstages.protected.includes(name) ? 'deployed, protected' : 'deployed',\n\t\t]),\n\t];\n\tconst width = Math.max(...rows.map(([name]) => name.length));\n\treturn rows\n\t\t.map(([name, kind]) => `${name.padEnd(width)} ${kind}`)\n\t\t.join('\\n');\n}\n\nexport async function stagesCommand(\n\toptions: StagesOptions,\n\tdeps: StagesDeps = {},\n): Promise<void> {\n\tconst env = deps.env ?? process.env;\n\tconst log = deps.log ?? ((line: string) => console.log(line));\n\n\tif (!options.githubOutput) {\n\t\tconst workspace = await loadWorkspaceSettings(deps.cwd);\n\t\tconst stages = stagesJson(workspace.stages);\n\t\tlog(options.json ? JSON.stringify(stages) : table(stages));\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst file = env.GITHUB_OUTPUT;\n\t\tif (!file) throw new GithubOutputNotSet();\n\n\t\tconst workspace = await loadWorkspaceSettings(deps.cwd);\n\t\tconst event = options.event || env.GITHUB_EVENT_NAME;\n\t\tif (!event) throw new GithubEventNotSet();\n\n\t\tconst outputs = githubOutputs(workspace, {\n\t\t\tevent,\n\t\t\t...(options.stage ? { stage: options.stage } : {}),\n\t\t});\n\t\tawait appendFile(file, formatGithubOutputs(outputs));\n\n\t\tlog(`${event}: build ${outputs.build}, deploy ${outputs.deploy}`);\n\t} catch (error) {\n\t\t// Annotated on the run, where whoever started it looks first.\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog(`::error title=gkm stages::${workflowCommandData(message)}`);\n\t\tthrow error;\n\t}\n}\n\n/** `--github-output` outside a GitHub Actions step, which sets the file. */\nexport class GithubOutputNotSet extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm stages --github-output writes to the file GITHUB_OUTPUT names, and it is not set. Run it in a GitHub Actions step, or set GITHUB_OUTPUT to a file to write to.',\n\t\t);\n\t\tthis.name = 'GithubOutputNotSet';\n\t}\n}\n\n/** No `--event`, and no `GITHUB_EVENT_NAME` to take it from. */\nexport class GithubEventNotSet extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm stages --github-output plans for an event, and none was given. Pass --event (push, release, workflow_dispatch), or run it in a GitHub Actions step, which sets GITHUB_EVENT_NAME.',\n\t\t);\n\t\tthis.name = 'GithubEventNotSet';\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { M as UndeclaredStage } from "./workspace-CJ0lKFq-.mjs";
|
|
2
2
|
import { loadWorkspaceSettings } from "./config.mjs";
|
|
3
3
|
import { appendFile } from "node:fs/promises";
|
|
4
4
|
//#region src/stages/plan.ts
|
|
@@ -171,4 +171,4 @@ var GithubEventNotSet = class extends Error {
|
|
|
171
171
|
//#endregion
|
|
172
172
|
export { stagesCommand };
|
|
173
173
|
|
|
174
|
-
//# sourceMappingURL=stages-
|
|
174
|
+
//# sourceMappingURL=stages-CnZRYmkI.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stages-DoHHz6pS.mjs","names":[],"sources":["../src/stages/plan.ts","../src/stages/index.ts"],"sourcesContent":["/**\n * Which stages a workflow run builds and deploys, from the event that started\n * it and the project's `stages`.\n *\n * The rules live here, in tested code, rather than in a workflow's YAML or in\n * the action that runs `gkm stages --github-output`: a project's deploy\n * workflow names no stage, and what it does for each event is decided by the\n * CLI the project installed.\n *\n * | event | build | deploy |\n * | ------------------- | ------------------ | ------------------------------- |\n * | `push` | every deployed | deployed, less the protected |\n * | `release` | none | the protected |\n * | `workflow_dispatch` | none | the one stage named |\n * | anything else | none | none |\n *\n * A push builds every deployed stage, protected ones included, so a release\n * deploys images built from the very commit its tag names; a release and a\n * manual run deploy what a push already built.\n */\n\nimport { UndeclaredStage } from '../workspace/stages.js';\nimport type { StagesConfig } from '../workspace/types.js';\n\nexport interface StagePlan {\n\t/** The stages whose images this run builds and pushes. */\n\tbuild: string[];\n\t/** The stages this run deploys. */\n\tdeploy: string[];\n}\n\nexport interface PlanInput {\n\t/** The GitHub event name: `push`, `release`, `workflow_dispatch`, … */\n\tevent: string;\n\t/** The stage a `workflow_dispatch` names. */\n\tstage?: string;\n}\n\nexport function planStages(stages: StagesConfig, input: PlanInput): StagePlan {\n\tconst kept = stages.protected ?? [];\n\n\tswitch (input.event) {\n\t\tcase 'push':\n\t\t\treturn {\n\t\t\t\tbuild: [...stages.deployed],\n\t\t\t\tdeploy: stages.deployed.filter((stage) => !kept.includes(stage)),\n\t\t\t};\n\t\tcase 'release':\n\t\t\treturn { build: [], deploy: [...kept] };\n\t\tcase 'workflow_dispatch': {\n\t\t\tconst stage = input.stage?.trim();\n\t\t\tif (!stage) throw new DispatchNamesNoStage(stages.deployed);\n\t\t\tif (!stages.deployed.includes(stage)) {\n\t\t\t\tthrow new UndeclaredStage(stage, stages.deployed);\n\t\t\t}\n\t\t\treturn { build: [], deploy: [stage] };\n\t\t}\n\t\tdefault:\n\t\t\treturn { build: [], deploy: [] };\n\t}\n}\n\n/**\n * A manual run with no stage. Refused rather than read as \"nothing to do\", so\n * a run started with the field left empty fails where it can be seen.\n */\nexport class DispatchNamesNoStage extends Error {\n\tconstructor(readonly deployed: readonly string[]) {\n\t\tsuper(\n\t\t\t`A manual run deploys one stage, and none was given. Run the workflow again with stage set to one of: ${deployed.join(', ') || 'nothing yet — gkm.config.ts deploys to no stage'}.`,\n\t\t);\n\t\tthis.name = 'DispatchNamesNoStage';\n\t}\n}\n","/**\n * `gkm stages` — the workspace's stages, as gkm.config.ts declares them.\n *\n * ```bash\n * gkm stages # a table\n * gkm stages --json # {\"local\":\"dev\",\"deployed\":[\"prod\"],\"protected\":[\"prod\"]}\n * gkm stages --github-output # a workflow run's build and deploy matrices\n * ```\n *\n * The config is read by the normal loader (and sandbox), so a workflow never\n * parses TypeScript to learn which stages exist. `--github-output` is what the\n * `geekmidas/toolbox/actions/stages` action runs: it writes the stages, and\n * which of them this run builds and deploys, to `$GITHUB_OUTPUT`.\n */\n\nimport { appendFile } from 'node:fs/promises';\nimport { loadWorkspaceSettings } from '../config.js';\nimport type { NormalizedWorkspace, StagesConfig } from '../workspace/types.js';\nimport { planStages } from './plan.js';\n\nexport { DispatchNamesNoStage, type PlanInput, planStages } from './plan.js';\n\nexport interface StagesOptions {\n\t/** Print the stages as JSON. */\n\tjson?: boolean;\n\t/** Write the outputs a deploy workflow reads to `$GITHUB_OUTPUT`. */\n\tgithubOutput?: boolean;\n\t/** The event to plan for; defaults to `GITHUB_EVENT_NAME`. */\n\tevent?: string;\n\t/** The stage a `workflow_dispatch` names. */\n\tstage?: string;\n}\n\nexport interface StagesDeps {\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\t/** Where lines go; stdout by default. */\n\tlog?: (line: string) => void;\n}\n\n/** The stages, as `gkm stages --json` prints them. */\nexport interface StagesJson {\n\tlocal: string;\n\tdeployed: string[];\n\tprotected: string[];\n}\n\nexport function stagesJson(stages: StagesConfig): StagesJson {\n\treturn {\n\t\tlocal: stages.local,\n\t\tdeployed: [...stages.deployed],\n\t\tprotected: [...(stages.protected ?? [])],\n\t};\n}\n\n/**\n * The region of the workspace's AWS secrets store — `ssm` or\n * `secrets-manager` — or `''` when its deployed stages keep their secrets\n * anywhere else. A job assumes the stage's AWS role only when this is set.\n */\nexport function awsSecretsRegion(workspace: NormalizedWorkspace): string {\n\tconst store = workspace.secrets?.store;\n\tif (typeof store !== 'object' || store === null) return '';\n\tif (store.provider === 'ssm' || store.provider === 'secrets-manager') {\n\t\treturn store.region;\n\t}\n\treturn '';\n}\n\n/**\n * Every output the stages action sets, each a string `fromJSON()` reads, for\n * one workflow run.\n */\nexport function githubOutputs(\n\tworkspace: NormalizedWorkspace,\n\tinput: { event: string; stage?: string },\n): Record<string, string> {\n\tconst stages = stagesJson(workspace.stages);\n\tconst plan = planStages(workspace.stages, input);\n\n\treturn {\n\t\tlocal: JSON.stringify(stages.local),\n\t\tdeployed: JSON.stringify(stages.deployed),\n\t\tprotected: JSON.stringify(stages.protected),\n\t\tbuild: JSON.stringify(plan.build),\n\t\tdeploy: JSON.stringify(plan.deploy),\n\t\t// A matrix over an empty list is an error on GitHub, so a job over one\n\t\t// is skipped on these instead.\n\t\t'has-build': String(plan.build.length > 0),\n\t\t'has-deploy': String(plan.deploy.length > 0),\n\t\t'aws-region': awsSecretsRegion(workspace),\n\t};\n}\n\n/** `name=value` lines, as `$GITHUB_OUTPUT` takes single-line values. */\nexport function formatGithubOutputs(outputs: Record<string, string>): string {\n\treturn Object.entries(outputs)\n\t\t.map(([name, value]) => `${name}=${value}\\n`)\n\t\t.join('');\n}\n\n/** A message as a workflow command's data: GitHub reads `%`, CR and LF escaped. */\nfunction workflowCommandData(message: string): string {\n\treturn message\n\t\t.replaceAll('%', '%25')\n\t\t.replaceAll('\\r', '%0D')\n\t\t.replaceAll('\\n', '%0A');\n}\n\nfunction table(stages: StagesJson): string {\n\tconst rows: [string, string][] = [\n\t\t['STAGE', 'KIND'],\n\t\t[stages.local, 'local'],\n\t\t...stages.deployed.map((name): [string, string] => [\n\t\t\tname,\n\t\t\tstages.protected.includes(name) ? 'deployed, protected' : 'deployed',\n\t\t]),\n\t];\n\tconst width = Math.max(...rows.map(([name]) => name.length));\n\treturn rows\n\t\t.map(([name, kind]) => `${name.padEnd(width)} ${kind}`)\n\t\t.join('\\n');\n}\n\nexport async function stagesCommand(\n\toptions: StagesOptions,\n\tdeps: StagesDeps = {},\n): Promise<void> {\n\tconst env = deps.env ?? process.env;\n\tconst log = deps.log ?? ((line: string) => console.log(line));\n\n\tif (!options.githubOutput) {\n\t\tconst workspace = await loadWorkspaceSettings(deps.cwd);\n\t\tconst stages = stagesJson(workspace.stages);\n\t\tlog(options.json ? JSON.stringify(stages) : table(stages));\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst file = env.GITHUB_OUTPUT;\n\t\tif (!file) throw new GithubOutputNotSet();\n\n\t\tconst workspace = await loadWorkspaceSettings(deps.cwd);\n\t\tconst event = options.event || env.GITHUB_EVENT_NAME;\n\t\tif (!event) throw new GithubEventNotSet();\n\n\t\tconst outputs = githubOutputs(workspace, {\n\t\t\tevent,\n\t\t\t...(options.stage ? { stage: options.stage } : {}),\n\t\t});\n\t\tawait appendFile(file, formatGithubOutputs(outputs));\n\n\t\tlog(`${event}: build ${outputs.build}, deploy ${outputs.deploy}`);\n\t} catch (error) {\n\t\t// Annotated on the run, where whoever started it looks first.\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog(`::error title=gkm stages::${workflowCommandData(message)}`);\n\t\tthrow error;\n\t}\n}\n\n/** `--github-output` outside a GitHub Actions step, which sets the file. */\nexport class GithubOutputNotSet extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm stages --github-output writes to the file GITHUB_OUTPUT names, and it is not set. Run it in a GitHub Actions step, or set GITHUB_OUTPUT to a file to write to.',\n\t\t);\n\t\tthis.name = 'GithubOutputNotSet';\n\t}\n}\n\n/** No `--event`, and no `GITHUB_EVENT_NAME` to take it from. */\nexport class GithubEventNotSet extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm stages --github-output plans for an event, and none was given. Pass --event (push, release, workflow_dispatch), or run it in a GitHub Actions step, which sets GITHUB_EVENT_NAME.',\n\t\t);\n\t\tthis.name = 'GithubEventNotSet';\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stages-CnZRYmkI.mjs","names":[],"sources":["../src/stages/plan.ts","../src/stages/index.ts"],"sourcesContent":["/**\n * Which stages a workflow run builds and deploys, from the event that started\n * it and the project's `stages`.\n *\n * The rules live here, in tested code, rather than in a workflow's YAML or in\n * the action that runs `gkm stages --github-output`: a project's deploy\n * workflow names no stage, and what it does for each event is decided by the\n * CLI the project installed.\n *\n * | event | build | deploy |\n * | ------------------- | ------------------ | ------------------------------- |\n * | `push` | every deployed | deployed, less the protected |\n * | `release` | none | the protected |\n * | `workflow_dispatch` | none | the one stage named |\n * | anything else | none | none |\n *\n * A push builds every deployed stage, protected ones included, so a release\n * deploys images built from the very commit its tag names; a release and a\n * manual run deploy what a push already built.\n */\n\nimport { UndeclaredStage } from '../workspace/stages.js';\nimport type { StagesConfig } from '../workspace/types.js';\n\nexport interface StagePlan {\n\t/** The stages whose images this run builds and pushes. */\n\tbuild: string[];\n\t/** The stages this run deploys. */\n\tdeploy: string[];\n}\n\nexport interface PlanInput {\n\t/** The GitHub event name: `push`, `release`, `workflow_dispatch`, … */\n\tevent: string;\n\t/** The stage a `workflow_dispatch` names. */\n\tstage?: string;\n}\n\nexport function planStages(stages: StagesConfig, input: PlanInput): StagePlan {\n\tconst kept = stages.protected ?? [];\n\n\tswitch (input.event) {\n\t\tcase 'push':\n\t\t\treturn {\n\t\t\t\tbuild: [...stages.deployed],\n\t\t\t\tdeploy: stages.deployed.filter((stage) => !kept.includes(stage)),\n\t\t\t};\n\t\tcase 'release':\n\t\t\treturn { build: [], deploy: [...kept] };\n\t\tcase 'workflow_dispatch': {\n\t\t\tconst stage = input.stage?.trim();\n\t\t\tif (!stage) throw new DispatchNamesNoStage(stages.deployed);\n\t\t\tif (!stages.deployed.includes(stage)) {\n\t\t\t\tthrow new UndeclaredStage(stage, stages.deployed);\n\t\t\t}\n\t\t\treturn { build: [], deploy: [stage] };\n\t\t}\n\t\tdefault:\n\t\t\treturn { build: [], deploy: [] };\n\t}\n}\n\n/**\n * A manual run with no stage. Refused rather than read as \"nothing to do\", so\n * a run started with the field left empty fails where it can be seen.\n */\nexport class DispatchNamesNoStage extends Error {\n\tconstructor(readonly deployed: readonly string[]) {\n\t\tsuper(\n\t\t\t`A manual run deploys one stage, and none was given. Run the workflow again with stage set to one of: ${deployed.join(', ') || 'nothing yet — gkm.config.ts deploys to no stage'}.`,\n\t\t);\n\t\tthis.name = 'DispatchNamesNoStage';\n\t}\n}\n","/**\n * `gkm stages` — the workspace's stages, as gkm.config.ts declares them.\n *\n * ```bash\n * gkm stages # a table\n * gkm stages --json # {\"local\":\"dev\",\"deployed\":[\"prod\"],\"protected\":[\"prod\"]}\n * gkm stages --github-output # a workflow run's build and deploy matrices\n * ```\n *\n * The config is read by the normal loader (and sandbox), so a workflow never\n * parses TypeScript to learn which stages exist. `--github-output` is what the\n * `geekmidas/toolbox/actions/stages` action runs: it writes the stages, and\n * which of them this run builds and deploys, to `$GITHUB_OUTPUT`.\n */\n\nimport { appendFile } from 'node:fs/promises';\nimport { loadWorkspaceSettings } from '../config.js';\nimport type { NormalizedWorkspace, StagesConfig } from '../workspace/types.js';\nimport { planStages } from './plan.js';\n\nexport { DispatchNamesNoStage, type PlanInput, planStages } from './plan.js';\n\nexport interface StagesOptions {\n\t/** Print the stages as JSON. */\n\tjson?: boolean;\n\t/** Write the outputs a deploy workflow reads to `$GITHUB_OUTPUT`. */\n\tgithubOutput?: boolean;\n\t/** The event to plan for; defaults to `GITHUB_EVENT_NAME`. */\n\tevent?: string;\n\t/** The stage a `workflow_dispatch` names. */\n\tstage?: string;\n}\n\nexport interface StagesDeps {\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\t/** Where lines go; stdout by default. */\n\tlog?: (line: string) => void;\n}\n\n/** The stages, as `gkm stages --json` prints them. */\nexport interface StagesJson {\n\tlocal: string;\n\tdeployed: string[];\n\tprotected: string[];\n}\n\nexport function stagesJson(stages: StagesConfig): StagesJson {\n\treturn {\n\t\tlocal: stages.local,\n\t\tdeployed: [...stages.deployed],\n\t\tprotected: [...(stages.protected ?? [])],\n\t};\n}\n\n/**\n * The region of the workspace's AWS secrets store — `ssm` or\n * `secrets-manager` — or `''` when its deployed stages keep their secrets\n * anywhere else. A job assumes the stage's AWS role only when this is set.\n */\nexport function awsSecretsRegion(workspace: NormalizedWorkspace): string {\n\tconst store = workspace.secrets?.store;\n\tif (typeof store !== 'object' || store === null) return '';\n\tif (store.provider === 'ssm' || store.provider === 'secrets-manager') {\n\t\treturn store.region;\n\t}\n\treturn '';\n}\n\n/**\n * Every output the stages action sets, each a string `fromJSON()` reads, for\n * one workflow run.\n */\nexport function githubOutputs(\n\tworkspace: NormalizedWorkspace,\n\tinput: { event: string; stage?: string },\n): Record<string, string> {\n\tconst stages = stagesJson(workspace.stages);\n\tconst plan = planStages(workspace.stages, input);\n\n\treturn {\n\t\tlocal: JSON.stringify(stages.local),\n\t\tdeployed: JSON.stringify(stages.deployed),\n\t\tprotected: JSON.stringify(stages.protected),\n\t\tbuild: JSON.stringify(plan.build),\n\t\tdeploy: JSON.stringify(plan.deploy),\n\t\t// A matrix over an empty list is an error on GitHub, so a job over one\n\t\t// is skipped on these instead.\n\t\t'has-build': String(plan.build.length > 0),\n\t\t'has-deploy': String(plan.deploy.length > 0),\n\t\t'aws-region': awsSecretsRegion(workspace),\n\t};\n}\n\n/** `name=value` lines, as `$GITHUB_OUTPUT` takes single-line values. */\nexport function formatGithubOutputs(outputs: Record<string, string>): string {\n\treturn Object.entries(outputs)\n\t\t.map(([name, value]) => `${name}=${value}\\n`)\n\t\t.join('');\n}\n\n/** A message as a workflow command's data: GitHub reads `%`, CR and LF escaped. */\nfunction workflowCommandData(message: string): string {\n\treturn message\n\t\t.replaceAll('%', '%25')\n\t\t.replaceAll('\\r', '%0D')\n\t\t.replaceAll('\\n', '%0A');\n}\n\nfunction table(stages: StagesJson): string {\n\tconst rows: [string, string][] = [\n\t\t['STAGE', 'KIND'],\n\t\t[stages.local, 'local'],\n\t\t...stages.deployed.map((name): [string, string] => [\n\t\t\tname,\n\t\t\tstages.protected.includes(name) ? 'deployed, protected' : 'deployed',\n\t\t]),\n\t];\n\tconst width = Math.max(...rows.map(([name]) => name.length));\n\treturn rows\n\t\t.map(([name, kind]) => `${name.padEnd(width)} ${kind}`)\n\t\t.join('\\n');\n}\n\nexport async function stagesCommand(\n\toptions: StagesOptions,\n\tdeps: StagesDeps = {},\n): Promise<void> {\n\tconst env = deps.env ?? process.env;\n\tconst log = deps.log ?? ((line: string) => console.log(line));\n\n\tif (!options.githubOutput) {\n\t\tconst workspace = await loadWorkspaceSettings(deps.cwd);\n\t\tconst stages = stagesJson(workspace.stages);\n\t\tlog(options.json ? JSON.stringify(stages) : table(stages));\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst file = env.GITHUB_OUTPUT;\n\t\tif (!file) throw new GithubOutputNotSet();\n\n\t\tconst workspace = await loadWorkspaceSettings(deps.cwd);\n\t\tconst event = options.event || env.GITHUB_EVENT_NAME;\n\t\tif (!event) throw new GithubEventNotSet();\n\n\t\tconst outputs = githubOutputs(workspace, {\n\t\t\tevent,\n\t\t\t...(options.stage ? { stage: options.stage } : {}),\n\t\t});\n\t\tawait appendFile(file, formatGithubOutputs(outputs));\n\n\t\tlog(`${event}: build ${outputs.build}, deploy ${outputs.deploy}`);\n\t} catch (error) {\n\t\t// Annotated on the run, where whoever started it looks first.\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog(`::error title=gkm stages::${workflowCommandData(message)}`);\n\t\tthrow error;\n\t}\n}\n\n/** `--github-output` outside a GitHub Actions step, which sets the file. */\nexport class GithubOutputNotSet extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm stages --github-output writes to the file GITHUB_OUTPUT names, and it is not set. Run it in a GitHub Actions step, or set GITHUB_OUTPUT to a file to write to.',\n\t\t);\n\t\tthis.name = 'GithubOutputNotSet';\n\t}\n}\n\n/** No `--event`, and no `GITHUB_EVENT_NAME` to take it from. */\nexport class GithubEventNotSet extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm stages --github-output plans for an event, and none was given. Pass --event (push, release, workflow_dispatch), or run it in a GitHub Actions step, which sets GITHUB_EVENT_NAME.',\n\t\t);\n\t\tthis.name = 'GithubEventNotSet';\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-DzRz8FgH.cjs");
|
|
3
|
-
const require_resolve = require("../resolve-
|
|
3
|
+
const require_resolve = require("../resolve-dlKPFNgv.cjs");
|
|
4
4
|
const require_provider = require("../provider-C9_-45d5.cjs");
|
|
5
5
|
exports.DeployTargetNotYetSupported = require_define.DeployTargetNotYetSupported;
|
|
6
6
|
exports.InvalidTargetOptions = require_resolve.InvalidTargetOptions;
|
package/dist/target/public.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as TargetOptions, G as DeployPhaseContext, J as DeployTarget, K as DeployRuntime, Q as TargetEvent, Tt as DeployIdentity, U as AnyDeployTarget, W as DeployFailure, X as DeployTargetEntry, Y as DeployTargetCapabilities, Z as DeployTargetLogger, at as DeployPhase, ct as AppDeployResult, dt as Credential, et as TargetOptionsIssue, ft as CredentialKind, ht as CredentialRequest, it as DeployEventType, lt as DeployResult, mt as CredentialProvider, nt as DeployEvent, ot as ResourceChange, pt as CredentialKinds, q as DeploySecrets, rt as DeployEventError, st as ResourceVia, tt as TargetOptionsSchema, ut as AwsCredential, wt as StageSecrets, xt as StateStore } from "../types-
|
|
2
|
-
import { a as TargetRuntimeMismatch, c as DeployTargetNotYetSupported, i as TargetPackageInvalid, l as UnknownDeployTarget, n as InvalidTargetOptions, o as ProviderRemoved, r as TargetEntryInvalid, s as TargetPackageNotFound, t as TargetRuntimeUndeclared } from "../runtime-
|
|
1
|
+
import { $ as TargetOptions, G as DeployPhaseContext, J as DeployTarget, K as DeployRuntime, Q as TargetEvent, Tt as DeployIdentity, U as AnyDeployTarget, W as DeployFailure, X as DeployTargetEntry, Y as DeployTargetCapabilities, Z as DeployTargetLogger, at as DeployPhase, ct as AppDeployResult, dt as Credential, et as TargetOptionsIssue, ft as CredentialKind, ht as CredentialRequest, it as DeployEventType, lt as DeployResult, mt as CredentialProvider, nt as DeployEvent, ot as ResourceChange, pt as CredentialKinds, q as DeploySecrets, rt as DeployEventError, st as ResourceVia, tt as TargetOptionsSchema, ut as AwsCredential, wt as StageSecrets, xt as StateStore } from "../types-NqX1Hz4P.cjs";
|
|
2
|
+
import { a as TargetRuntimeMismatch, c as DeployTargetNotYetSupported, i as TargetPackageInvalid, l as UnknownDeployTarget, n as InvalidTargetOptions, o as ProviderRemoved, r as TargetEntryInvalid, s as TargetPackageNotFound, t as TargetRuntimeUndeclared } from "../runtime-CN_kmqxo.cjs";
|
|
3
3
|
//#region src/target/define.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* A deploy target, typed from what it is written with: the options from its
|
package/dist/target/public.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as TargetOptions, G as DeployPhaseContext, J as DeployTarget, K as DeployRuntime, Q as TargetEvent, Tt as DeployIdentity, U as AnyDeployTarget, W as DeployFailure, X as DeployTargetEntry, Y as DeployTargetCapabilities, Z as DeployTargetLogger, at as DeployPhase, ct as AppDeployResult, dt as Credential, et as TargetOptionsIssue, ft as CredentialKind, ht as CredentialRequest, it as DeployEventType, lt as DeployResult, mt as CredentialProvider, nt as DeployEvent, ot as ResourceChange, pt as CredentialKinds, q as DeploySecrets, rt as DeployEventError, st as ResourceVia, tt as TargetOptionsSchema, ut as AwsCredential, wt as StageSecrets, xt as StateStore } from "../types-
|
|
2
|
-
import { a as TargetRuntimeMismatch, c as DeployTargetNotYetSupported, i as TargetPackageInvalid, l as UnknownDeployTarget, n as InvalidTargetOptions, o as ProviderRemoved, r as TargetEntryInvalid, s as TargetPackageNotFound, t as TargetRuntimeUndeclared } from "../runtime-
|
|
1
|
+
import { $ as TargetOptions, G as DeployPhaseContext, J as DeployTarget, K as DeployRuntime, Q as TargetEvent, Tt as DeployIdentity, U as AnyDeployTarget, W as DeployFailure, X as DeployTargetEntry, Y as DeployTargetCapabilities, Z as DeployTargetLogger, at as DeployPhase, ct as AppDeployResult, dt as Credential, et as TargetOptionsIssue, ft as CredentialKind, ht as CredentialRequest, it as DeployEventType, lt as DeployResult, mt as CredentialProvider, nt as DeployEvent, ot as ResourceChange, pt as CredentialKinds, q as DeploySecrets, rt as DeployEventError, st as ResourceVia, tt as TargetOptionsSchema, ut as AwsCredential, wt as StageSecrets, xt as StateStore } from "../types-ZHuZ89hv.mjs";
|
|
2
|
+
import { a as TargetRuntimeMismatch, c as DeployTargetNotYetSupported, i as TargetPackageInvalid, l as UnknownDeployTarget, n as InvalidTargetOptions, o as ProviderRemoved, r as TargetEntryInvalid, s as TargetPackageNotFound, t as TargetRuntimeUndeclared } from "../runtime-Cjvon3oj.mjs";
|
|
3
3
|
//#region src/target/define.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* A deploy target, typed from what it is written with: the options from its
|
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-DR8xGYfL.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-Dv_weZoh.mjs";
|
|
3
3
|
import { t as ProviderRemoved } from "../provider-q4lsYzYG.mjs";
|
|
4
4
|
export { DeployTargetNotYetSupported, InvalidTargetOptions, ProviderRemoved, TargetEntryInvalid, TargetPackageInvalid, TargetPackageNotFound, TargetRuntimeMismatch, TargetRuntimeUndeclared, UnknownDeployTarget, defineTarget, isDeployTarget };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as storeDokployCredentials, n as chainCredentials, r as storedCredentials } from "./credentials-CnVqoXMb.mjs";
|
|
2
2
|
import { d as output } from "./discover-BPelzeGW.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { c as prompt, o as validateDokployToken, s as canPrompt } from "./index.mjs";
|
|
4
4
|
//#region src/deploy/terminal.ts
|
|
5
5
|
/**
|
|
6
6
|
* The credentials `gkm deploy` can ask a person for. CLI layer only.
|
|
@@ -81,4 +81,4 @@ function terminalCredentials(base = storedCredentials()) {
|
|
|
81
81
|
//#endregion
|
|
82
82
|
export { terminalCredentials };
|
|
83
83
|
|
|
84
|
-
//# sourceMappingURL=terminal-
|
|
84
|
+
//# sourceMappingURL=terminal-D8TPK7o9.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-
|
|
1
|
+
{"version":3,"file":"terminal-D8TPK7o9.mjs","names":[],"sources":["../src/deploy/terminal.ts"],"sourcesContent":["/**\n * The credentials `gkm deploy` can ask a person for. CLI layer only.\n *\n * `deploy()` asks its `CredentialProvider` and stops with `MissingCredential`\n * when the answer is nothing. At a terminal, `gkm deploy` would rather ask:\n * this provider reads the environment and the stored login first, and only\n * when both are empty prompts — and stores a Dokploy login it validated, so\n * the next deploy does not ask again. With no terminal it answers nothing, and\n * the deploy says what was missing instead of waiting on a prompt nobody sees.\n */\n\nimport { validateDokployToken } from '../auth';\nimport { storeDokployCredentials } from '../auth/credentials';\nimport { output } from '../output';\nimport { canPrompt, prompt } from '../prompt';\nimport {\n\ttype CredentialProvider,\n\ttype CredentialRequest,\n\tchainCredentials,\n\tstoredCredentials,\n} from './credentials';\n\nconst logger = output;\n\n/** What answers a deploy prompt without a terminal. */\nconst INSTEAD = 'Please configure manually.';\n\n/** The Dokploy URL typed at the prompt is not a URL. */\nexport class DokployEndpointInvalid extends Error {\n\tconstructor(readonly endpoint: string) {\n\t\tsuper('Invalid URL format');\n\t\tthis.name = 'DokployEndpointInvalid';\n\t}\n}\n\n/** Dokploy refused the token typed at the prompt; nothing was stored. */\nexport class DokployTokenRejected extends Error {\n\tconstructor(readonly endpoint: string) {\n\t\tsuper('Invalid credentials. Please check your token.');\n\t\tthis.name = 'DokployTokenRejected';\n\t}\n}\n\n/** Asks the person at the terminal for what nothing else supplied. */\nfunction asking(): CredentialProvider {\n\treturn {\n\t\tasync get(asked) {\n\t\t\tif (!canPrompt()) return undefined;\n\t\t\t// The union, so checking `kind` narrows it: `K` itself never is.\n\t\t\tconst request = asked as CredentialRequest;\n\n\t\t\tif (request.kind === 'dokploy') {\n\t\t\t\tlogger.log(\"\\n📋 Dokploy credentials not found. Let's set them up.\");\n\t\t\t\tconst endpoint = (\n\t\t\t\t\tawait prompt('Dokploy URL (e.g., https://dokploy.example.com): ', {\n\t\t\t\t\t\tinstead: INSTEAD,\n\t\t\t\t\t})\n\t\t\t\t).replace(/\\/$/, '');\n\n\t\t\t\ttry {\n\t\t\t\t\tnew URL(endpoint);\n\t\t\t\t} catch {\n\t\t\t\t\tthrow new DokployEndpointInvalid(endpoint);\n\t\t\t\t}\n\n\t\t\t\tlogger.log(`\\nGenerate a token at: ${endpoint}/settings/profile\\n`);\n\t\t\t\tconst token = await prompt('API Token: ', {\n\t\t\t\t\thidden: true,\n\t\t\t\t\tinstead: INSTEAD,\n\t\t\t\t});\n\n\t\t\t\tlogger.log('\\nValidating credentials...');\n\t\t\t\tif (!(await validateDokployToken(endpoint, token))) {\n\t\t\t\t\tthrow new DokployTokenRejected(endpoint);\n\t\t\t\t}\n\n\t\t\t\tawait storeDokployCredentials(token, endpoint);\n\t\t\t\tlogger.log('✓ Credentials saved');\n\t\t\t\treturn { endpoint, token } as never;\n\t\t\t}\n\n\t\t\t// AWS is a profile or a role, set up with the AWS CLI — not keys\n\t\t\t// typed into a deploy.\n\t\t\tif (request.kind !== 'registry') return undefined;\n\n\t\t\tlogger.log(\n\t\t\t\t` Dokploy has no registry for ${request.url}. Let's create one.`,\n\t\t\t);\n\t\t\tconst username = await prompt('Registry username: ', {\n\t\t\t\tinstead: INSTEAD,\n\t\t\t});\n\t\t\tconst password = await prompt('Registry password/token: ', {\n\t\t\t\thidden: true,\n\t\t\t\tinstead: INSTEAD,\n\t\t\t});\n\t\t\treturn { username, password } as never;\n\t\t},\n\t};\n}\n\n/**\n * The environment, then the stored login, then — at a terminal — the person\n * running the command.\n */\nexport function terminalCredentials(\n\tbase: CredentialProvider = storedCredentials(),\n): CredentialProvider {\n\treturn chainCredentials(base, asking());\n}\n"],"mappings":";;;;;;;;;;;;;;AAsBA,MAAM,SAAS;;AAGf,MAAM,UAAU;;AAGhB,IAAa,yBAAb,cAA4C,MAAM;CAC5B;CAArB,YAAY,UAA2B;EACtC,MAAM,oBAAoB;EADN,KAAA,WAAA;EAEpB,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,uBAAb,cAA0C,MAAM;CAC1B;CAArB,YAAY,UAA2B;EACtC,MAAM,+CAA+C;EADjC,KAAA,WAAA;EAEpB,KAAK,OAAO;CACb;AACD;;AAGA,SAAS,SAA6B;CACrC,OAAO,EACN,MAAM,IAAI,OAAO;EAChB,IAAI,CAAC,UAAU,GAAG,OAAO,KAAA;EAEzB,MAAM,UAAU;EAEhB,IAAI,QAAQ,SAAS,WAAW;GAC/B,OAAO,IAAI,wDAAwD;GACnE,MAAM,YACL,MAAM,OAAO,qDAAqD,EACjE,SAAS,QACV,CAAC,EAAA,CACA,QAAQ,OAAO,EAAE;GAEnB,IAAI;IACH,IAAI,IAAI,QAAQ;GACjB,QAAQ;IACP,MAAM,IAAI,uBAAuB,QAAQ;GAC1C;GAEA,OAAO,IAAI,0BAA0B,SAAS,oBAAoB;GAClE,MAAM,QAAQ,MAAM,OAAO,eAAe;IACzC,QAAQ;IACR,SAAS;GACV,CAAC;GAED,OAAO,IAAI,6BAA6B;GACxC,IAAI,CAAE,MAAM,qBAAqB,UAAU,KAAK,GAC/C,MAAM,IAAI,qBAAqB,QAAQ;GAGxC,MAAM,wBAAwB,OAAO,QAAQ;GAC7C,OAAO,IAAI,qBAAqB;GAChC,OAAO;IAAE;IAAU;GAAM;EAC1B;EAIA,IAAI,QAAQ,SAAS,YAAY,OAAO,KAAA;EAExC,OAAO,IACN,kCAAkC,QAAQ,IAAI,oBAC/C;EAQA,OAAO;GAAE,UAAA,MAPc,OAAO,uBAAuB,EACpD,SAAS,QACV,CAAC;GAKkB,UAAA,MAJI,OAAO,6BAA6B;IAC1D,QAAQ;IACR,SAAS;GACV,CAAC;EAC2B;CAC7B,EACD;AACD;;;;;AAMA,SAAgB,oBACf,OAA2B,kBAAkB,GACxB;CACrB,OAAO,iBAAiB,MAAM,OAAO,CAAC;AACvC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_credentials = require("./credentials-
|
|
1
|
+
const require_credentials = require("./credentials-BKfIugu2.cjs");
|
|
2
2
|
const require_discover = require("./discover-D1It5r_h.cjs");
|
|
3
3
|
const require_index = require("./index.cjs");
|
|
4
4
|
//#region src/deploy/terminal.ts
|
|
@@ -81,4 +81,4 @@ function terminalCredentials(base = require_credentials.storedCredentials()) {
|
|
|
81
81
|
//#endregion
|
|
82
82
|
exports.terminalCredentials = terminalCredentials;
|
|
83
83
|
|
|
84
|
-
//# sourceMappingURL=terminal-
|
|
84
|
+
//# sourceMappingURL=terminal-DuapRxh6.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-
|
|
1
|
+
{"version":3,"file":"terminal-DuapRxh6.cjs","names":["output","canPrompt","prompt","validateDokployToken","storeDokployCredentials","storedCredentials","chainCredentials"],"sources":["../src/deploy/terminal.ts"],"sourcesContent":["/**\n * The credentials `gkm deploy` can ask a person for. CLI layer only.\n *\n * `deploy()` asks its `CredentialProvider` and stops with `MissingCredential`\n * when the answer is nothing. At a terminal, `gkm deploy` would rather ask:\n * this provider reads the environment and the stored login first, and only\n * when both are empty prompts — and stores a Dokploy login it validated, so\n * the next deploy does not ask again. With no terminal it answers nothing, and\n * the deploy says what was missing instead of waiting on a prompt nobody sees.\n */\n\nimport { validateDokployToken } from '../auth';\nimport { storeDokployCredentials } from '../auth/credentials';\nimport { output } from '../output';\nimport { canPrompt, prompt } from '../prompt';\nimport {\n\ttype CredentialProvider,\n\ttype CredentialRequest,\n\tchainCredentials,\n\tstoredCredentials,\n} from './credentials';\n\nconst logger = output;\n\n/** What answers a deploy prompt without a terminal. */\nconst INSTEAD = 'Please configure manually.';\n\n/** The Dokploy URL typed at the prompt is not a URL. */\nexport class DokployEndpointInvalid extends Error {\n\tconstructor(readonly endpoint: string) {\n\t\tsuper('Invalid URL format');\n\t\tthis.name = 'DokployEndpointInvalid';\n\t}\n}\n\n/** Dokploy refused the token typed at the prompt; nothing was stored. */\nexport class DokployTokenRejected extends Error {\n\tconstructor(readonly endpoint: string) {\n\t\tsuper('Invalid credentials. Please check your token.');\n\t\tthis.name = 'DokployTokenRejected';\n\t}\n}\n\n/** Asks the person at the terminal for what nothing else supplied. */\nfunction asking(): CredentialProvider {\n\treturn {\n\t\tasync get(asked) {\n\t\t\tif (!canPrompt()) return undefined;\n\t\t\t// The union, so checking `kind` narrows it: `K` itself never is.\n\t\t\tconst request = asked as CredentialRequest;\n\n\t\t\tif (request.kind === 'dokploy') {\n\t\t\t\tlogger.log(\"\\n📋 Dokploy credentials not found. Let's set them up.\");\n\t\t\t\tconst endpoint = (\n\t\t\t\t\tawait prompt('Dokploy URL (e.g., https://dokploy.example.com): ', {\n\t\t\t\t\t\tinstead: INSTEAD,\n\t\t\t\t\t})\n\t\t\t\t).replace(/\\/$/, '');\n\n\t\t\t\ttry {\n\t\t\t\t\tnew URL(endpoint);\n\t\t\t\t} catch {\n\t\t\t\t\tthrow new DokployEndpointInvalid(endpoint);\n\t\t\t\t}\n\n\t\t\t\tlogger.log(`\\nGenerate a token at: ${endpoint}/settings/profile\\n`);\n\t\t\t\tconst token = await prompt('API Token: ', {\n\t\t\t\t\thidden: true,\n\t\t\t\t\tinstead: INSTEAD,\n\t\t\t\t});\n\n\t\t\t\tlogger.log('\\nValidating credentials...');\n\t\t\t\tif (!(await validateDokployToken(endpoint, token))) {\n\t\t\t\t\tthrow new DokployTokenRejected(endpoint);\n\t\t\t\t}\n\n\t\t\t\tawait storeDokployCredentials(token, endpoint);\n\t\t\t\tlogger.log('✓ Credentials saved');\n\t\t\t\treturn { endpoint, token } as never;\n\t\t\t}\n\n\t\t\t// AWS is a profile or a role, set up with the AWS CLI — not keys\n\t\t\t// typed into a deploy.\n\t\t\tif (request.kind !== 'registry') return undefined;\n\n\t\t\tlogger.log(\n\t\t\t\t` Dokploy has no registry for ${request.url}. Let's create one.`,\n\t\t\t);\n\t\t\tconst username = await prompt('Registry username: ', {\n\t\t\t\tinstead: INSTEAD,\n\t\t\t});\n\t\t\tconst password = await prompt('Registry password/token: ', {\n\t\t\t\thidden: true,\n\t\t\t\tinstead: INSTEAD,\n\t\t\t});\n\t\t\treturn { username, password } as never;\n\t\t},\n\t};\n}\n\n/**\n * The environment, then the stored login, then — at a terminal — the person\n * running the command.\n */\nexport function terminalCredentials(\n\tbase: CredentialProvider = storedCredentials(),\n): CredentialProvider {\n\treturn chainCredentials(base, asking());\n}\n"],"mappings":";;;;;;;;;;;;;;AAsBA,MAAM,SAASA,iBAAAA;;AAGf,MAAM,UAAU;;AAGhB,IAAa,yBAAb,cAA4C,MAAM;CAC5B;CAArB,YAAY,UAA2B;EACtC,MAAM,oBAAoB;EADN,KAAA,WAAA;EAEpB,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,uBAAb,cAA0C,MAAM;CAC1B;CAArB,YAAY,UAA2B;EACtC,MAAM,+CAA+C;EADjC,KAAA,WAAA;EAEpB,KAAK,OAAO;CACb;AACD;;AAGA,SAAS,SAA6B;CACrC,OAAO,EACN,MAAM,IAAI,OAAO;EAChB,IAAI,CAACC,cAAAA,UAAU,GAAG,OAAO,KAAA;EAEzB,MAAM,UAAU;EAEhB,IAAI,QAAQ,SAAS,WAAW;GAC/B,OAAO,IAAI,wDAAwD;GACnE,MAAM,YACL,MAAMC,cAAAA,OAAO,qDAAqD,EACjE,SAAS,QACV,CAAC,EAAA,CACA,QAAQ,OAAO,EAAE;GAEnB,IAAI;IACH,IAAI,IAAI,QAAQ;GACjB,QAAQ;IACP,MAAM,IAAI,uBAAuB,QAAQ;GAC1C;GAEA,OAAO,IAAI,0BAA0B,SAAS,oBAAoB;GAClE,MAAM,QAAQ,MAAMA,cAAAA,OAAO,eAAe;IACzC,QAAQ;IACR,SAAS;GACV,CAAC;GAED,OAAO,IAAI,6BAA6B;GACxC,IAAI,CAAE,MAAMC,cAAAA,qBAAqB,UAAU,KAAK,GAC/C,MAAM,IAAI,qBAAqB,QAAQ;GAGxC,MAAMC,oBAAAA,wBAAwB,OAAO,QAAQ;GAC7C,OAAO,IAAI,qBAAqB;GAChC,OAAO;IAAE;IAAU;GAAM;EAC1B;EAIA,IAAI,QAAQ,SAAS,YAAY,OAAO,KAAA;EAExC,OAAO,IACN,kCAAkC,QAAQ,IAAI,oBAC/C;EAQA,OAAO;GAAE,UAAA,MAPcF,cAAAA,OAAO,uBAAuB,EACpD,SAAS,QACV,CAAC;GAKkB,UAAA,MAJIA,cAAAA,OAAO,6BAA6B;IAC1D,QAAQ;IACR,SAAS;GACV,CAAC;EAC2B;CAC7B,EACD;AACD;;;;;AAMA,SAAgB,oBACf,OAA2BG,oBAAAA,kBAAkB,GACxB;CACrB,OAAOC,oBAAAA,iBAAiB,MAAM,OAAO,CAAC;AACvC"}
|