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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/{SubscriberGenerator-DZk_pKDh.mjs → SubscriberGenerator-5looQ3gB.mjs} +18 -2
  2. package/dist/SubscriberGenerator-5looQ3gB.mjs.map +1 -0
  3. package/dist/{SubscriberGenerator-Bixm28fN.cjs → SubscriberGenerator-CajwOa5W.cjs} +29 -1
  4. package/dist/SubscriberGenerator-CajwOa5W.cjs.map +1 -0
  5. package/dist/{add-BiPlBUq3.mjs → add-BTH7WaH3.mjs} +2 -2
  6. package/dist/{add-BiPlBUq3.mjs.map → add-BTH7WaH3.mjs.map} +1 -1
  7. package/dist/{add-C1ojB2Yy.cjs → add-Djrr12Xc.cjs} +2 -2
  8. package/dist/{add-C1ojB2Yy.cjs.map → add-Djrr12Xc.cjs.map} +1 -1
  9. package/dist/{bundler-C2LZoLdP.mjs → bundler-BUojOkof.mjs} +5 -5
  10. package/dist/{bundler-C2LZoLdP.mjs.map → bundler-BUojOkof.mjs.map} +1 -1
  11. package/dist/{bundler-C35h0WKt.cjs → bundler-utHuESvL.cjs} +5 -5
  12. package/dist/{bundler-C35h0WKt.cjs.map → bundler-utHuESvL.cjs.map} +1 -1
  13. package/dist/{cli-DJVL2sL9.cjs → cli-Bx0sUzxH.cjs} +3 -3
  14. package/dist/{cli-DJVL2sL9.cjs.map → cli-Bx0sUzxH.cjs.map} +1 -1
  15. package/dist/{cli-BfdfyTF6.mjs → cli-CyYceKTS.mjs} +3 -3
  16. package/dist/{cli-BfdfyTF6.mjs.map → cli-CyYceKTS.mjs.map} +1 -1
  17. package/dist/{compose-9II-0IsK.mjs → compose-DZp8DJ0A.mjs} +202 -10
  18. package/dist/compose-DZp8DJ0A.mjs.map +1 -0
  19. package/dist/{compose-CNnw8meq.cjs → compose-iuw8wfHk.cjs} +202 -10
  20. package/dist/compose-iuw8wfHk.cjs.map +1 -0
  21. package/dist/{config-RLuR2o9A.d.cts → config-B38YWicj.d.cts} +3 -3
  22. package/dist/{config-B8OR_TrO.d.mts.map → config-B38YWicj.d.cts.map} +1 -1
  23. package/dist/{config-B8OR_TrO.d.mts → config-DA0kRTQe.d.mts} +3 -3
  24. package/dist/{config-RLuR2o9A.d.cts.map → config-DA0kRTQe.d.mts.map} +1 -1
  25. package/dist/config.d.cts +3 -3
  26. package/dist/config.d.mts +3 -3
  27. package/dist/deploy/public.cjs +5 -5
  28. package/dist/deploy/public.d.cts +3 -3
  29. package/dist/deploy/public.d.mts +3 -3
  30. package/dist/deploy/public.mjs +5 -5
  31. package/dist/{deploy-nmov4j0B.mjs → deploy-C17K7Imy.mjs} +3 -3
  32. package/dist/{deploy-nmov4j0B.mjs.map → deploy-C17K7Imy.mjs.map} +1 -1
  33. package/dist/{deploy-BitZLdnY.cjs → deploy-eWorhvQp.cjs} +3 -3
  34. package/dist/{deploy-BitZLdnY.cjs.map → deploy-eWorhvQp.cjs.map} +1 -1
  35. package/dist/{dokploy-CZ1vZt21.mjs → dokploy-DKa49Nqm.mjs} +2 -2
  36. package/dist/{dokploy-CZ1vZt21.mjs.map → dokploy-DKa49Nqm.mjs.map} +1 -1
  37. package/dist/{dokploy-Ce_VTytN.cjs → dokploy-DKzcI-RL.cjs} +2 -2
  38. package/dist/{dokploy-Ce_VTytN.cjs.map → dokploy-DKzcI-RL.cjs.map} +1 -1
  39. package/dist/drivers-BdvtCKc7.cjs +200 -0
  40. package/dist/drivers-BdvtCKc7.cjs.map +1 -0
  41. package/dist/drivers-Ch1rJitn.mjs +177 -0
  42. package/dist/drivers-Ch1rJitn.mjs.map +1 -0
  43. package/dist/{encryption-CDGRdcMr.cjs → encryption-DSkJdoyk.cjs} +2 -2
  44. package/dist/{encryption-CDGRdcMr.cjs.map → encryption-DSkJdoyk.cjs.map} +1 -1
  45. package/dist/{encryption-CmfPICSN.mjs → encryption-sTMz3aWi.mjs} +2 -2
  46. package/dist/{encryption-CmfPICSN.mjs.map → encryption-sTMz3aWi.mjs.map} +1 -1
  47. package/dist/{engine-B37qBO5W.mjs → engine-B1b4I34Z.mjs} +2 -2
  48. package/dist/{engine-B37qBO5W.mjs.map → engine-B1b4I34Z.mjs.map} +1 -1
  49. package/dist/{engine-DL2-PW6m.cjs → engine-Bh76sAHy.cjs} +2 -2
  50. package/dist/{engine-DL2-PW6m.cjs.map → engine-Bh76sAHy.cjs.map} +1 -1
  51. package/dist/{github-C6qF8kHg.cjs → github-BvN3CfxM.cjs} +2 -2
  52. package/dist/{github-C6qF8kHg.cjs.map → github-BvN3CfxM.cjs.map} +1 -1
  53. package/dist/{github-hjHHm7jM.mjs → github-C9ODoyZU.mjs} +2 -2
  54. package/dist/{github-hjHHm7jM.mjs.map → github-C9ODoyZU.mjs.map} +1 -1
  55. package/dist/{index-DHyTqGG3.d.mts → index-B-1lLGzy.d.mts} +2 -2
  56. package/dist/{index-DHyTqGG3.d.mts.map → index-B-1lLGzy.d.mts.map} +1 -1
  57. package/dist/{index-Dg-UZ4Hm.d.cts → index-vMidfqhU.d.cts} +2 -2
  58. package/dist/{index-Dg-UZ4Hm.d.cts.map → index-vMidfqhU.d.cts.map} +1 -1
  59. package/dist/index.cjs +59 -47
  60. package/dist/index.cjs.map +1 -1
  61. package/dist/index.mjs +57 -45
  62. package/dist/index.mjs.map +1 -1
  63. package/dist/{migrate-kA_j1TPK.cjs → migrate-B0FCZNkp.cjs} +2 -2
  64. package/dist/{migrate-kA_j1TPK.cjs.map → migrate-B0FCZNkp.cjs.map} +1 -1
  65. package/dist/{migrate-CFFC3esE.mjs → migrate-CTtJhGV9.mjs} +2 -2
  66. package/dist/{migrate-CFFC3esE.mjs.map → migrate-CTtJhGV9.mjs.map} +1 -1
  67. package/dist/openapi.d.cts +1 -1
  68. package/dist/openapi.d.mts +1 -1
  69. package/dist/{ready-BIuMw6wH.cjs → ready-BB9xS4S9.cjs} +6 -188
  70. package/dist/ready-BB9xS4S9.cjs.map +1 -0
  71. package/dist/{ready-C0U0NYfB.mjs → ready-ZZoNskrr.mjs} +5 -169
  72. package/dist/ready-ZZoNskrr.mjs.map +1 -0
  73. package/dist/reconcile/public.d.cts +1 -1
  74. package/dist/reconcile/public.d.mts +1 -1
  75. package/dist/{resolve-CV4eQkDa.mjs → resolve-DSAXj_47.mjs} +3 -3
  76. package/dist/{resolve-CV4eQkDa.mjs.map → resolve-DSAXj_47.mjs.map} +1 -1
  77. package/dist/{resolve-E5RfY0bm.cjs → resolve-D_BU-wKA.cjs} +3 -3
  78. package/dist/{resolve-E5RfY0bm.cjs.map → resolve-D_BU-wKA.cjs.map} +1 -1
  79. package/dist/{rollback-C-V-Rf4a.mjs → rollback-CWYTUhfW.mjs} +3 -3
  80. package/dist/{rollback-C-V-Rf4a.mjs.map → rollback-CWYTUhfW.mjs.map} +1 -1
  81. package/dist/{rollback-QGwomD64.cjs → rollback-DJy3t696.cjs} +3 -3
  82. package/dist/{rollback-QGwomD64.cjs.map → rollback-DJy3t696.cjs.map} +1 -1
  83. package/dist/{runtime-CWYLIeCU.d.mts → runtime-BzZD5HHi.d.cts} +2 -2
  84. package/dist/{runtime-CWYLIeCU.d.mts.map → runtime-BzZD5HHi.d.cts.map} +1 -1
  85. package/dist/{runtime-CmuQOT6A.d.cts → runtime-Cm6J6L7n.d.mts} +2 -2
  86. package/dist/{runtime-CmuQOT6A.d.cts.map → runtime-Cm6J6L7n.d.mts.map} +1 -1
  87. package/dist/{state-DMjFPOhf.cjs → state-DCwRWyh-.cjs} +16 -11
  88. package/dist/state-DCwRWyh-.cjs.map +1 -0
  89. package/dist/{state-DtS2HEfi.mjs → state-DpkxSwzI.mjs} +16 -11
  90. package/dist/state-DpkxSwzI.mjs.map +1 -0
  91. package/dist/target/public.cjs +1 -1
  92. package/dist/target/public.d.cts +2 -2
  93. package/dist/target/public.d.mts +2 -2
  94. package/dist/target/public.mjs +1 -1
  95. package/dist/{types-BLdCL4fB.d.mts → types-Dp9lgx_S.d.mts} +6 -2
  96. package/dist/{types-BLdCL4fB.d.mts.map → types-Dp9lgx_S.d.mts.map} +1 -1
  97. package/dist/{types-DwCQCkC-.d.cts → types-TzCChNrA.d.cts} +6 -2
  98. package/dist/{types-DwCQCkC-.d.cts.map → types-TzCChNrA.d.cts.map} +1 -1
  99. package/dist/vitest.cjs +1 -1
  100. package/dist/vitest.mjs +1 -1
  101. package/dist/workspace/index.d.cts +2 -2
  102. package/dist/workspace/index.d.mts +2 -2
  103. package/package.json +15 -15
  104. package/dist/SubscriberGenerator-Bixm28fN.cjs.map +0 -1
  105. package/dist/SubscriberGenerator-DZk_pKDh.mjs.map +0 -1
  106. package/dist/compose-9II-0IsK.mjs.map +0 -1
  107. package/dist/compose-CNnw8meq.cjs.map +0 -1
  108. package/dist/ready-BIuMw6wH.cjs.map +0 -1
  109. package/dist/ready-C0U0NYfB.mjs.map +0 -1
  110. package/dist/state-DMjFPOhf.cjs.map +0 -1
  111. package/dist/state-DtS2HEfi.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  const require_workspace = require("./workspace-vdrcRI-m.cjs");
2
2
  const require_config = require("./config.cjs");
3
- const require_state = require("./state-DMjFPOhf.cjs");
3
+ const require_state = require("./state-DCwRWyh-.cjs");
4
4
  let node_child_process = require("node:child_process");
5
5
  let _aws_sdk_client_iam = require("@aws-sdk/client-iam");
6
6
  //#region src/deploy/github.ts
@@ -210,4 +210,4 @@ var SsoSessionExpired = class extends Error {
210
210
  //#endregion
211
211
  exports.deployGithubCommand = deployGithubCommand;
212
212
 
213
- //# sourceMappingURL=github-C6qF8kHg.cjs.map
213
+ //# sourceMappingURL=github-BvN3CfxM.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"github-C6qF8kHg.cjs","names":["execFileSync","ListOpenIDConnectProvidersCommand","CreateOpenIDConnectProviderCommand","GetRoleCommand","UpdateAssumeRolePolicyCommand","CreateRoleCommand","AttachRolePolicyCommand","IAMClient","loadWorkspaceConfig","readKey","keystoreProject","getKeyPath","isRemoteStore","secretsStoreFor"],"sources":["../src/deploy/github.ts"],"sourcesContent":["/**\n * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.\n *\n * A stage usually lives in its own AWS account — staging in one, production in\n * another — so this runs once per stage, with that account's profile:\n *\n * gkm deploy:github --stage staging --profile acme-dev\n * gkm deploy:github --stage prod --profile acme-prod\n *\n * In the stage's account it makes sure GitHub's OIDC provider exists, and a\n * role that only this repository's `<stage>` environment can assume. On\n * GitHub it creates that environment and gives it what the generated deploy\n * workflow reads: `AWS_ROLE_ARN`, and — when the stage's secrets are kept in\n * the local file — its key as `GKM_SECRETS_KEY`. With `secrets.store` set to\n * SSM the deploy job pulls them with the role instead, so no key is handed to\n * GitHub, and this pushes the stage's local secrets to SSM in the same account,\n * with the same profile. Re-running it converges rather than duplicating.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListOpenIDConnectProvidersCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { getKeyPath, keystoreProject, readKey } from '../secrets/keystore.js';\nimport { isRemoteStore, secretsStoreFor } from '../secrets/store.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default —\n * said out loud in the output, and replaceable with `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\nexport interface DeployGithubOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account. */\n\tprofile?: string;\n\t/** `owner/name`; defaults to the repository `gh` sees here. */\n\trepo?: string;\n\t/** The policy the deploy role gets. */\n\tpolicyArn?: string;\n\tdryRun?: boolean;\n}\n\n/** Runs `gh` with the given arguments and optional stdin, returning stdout. */\nexport type Gh = (args: string[], input?: string) => string;\n\nconst defaultGh: Gh = (args, input) =>\n\texecFileSync('gh', args, {\n\t\tencoding: 'utf-8',\n\t\t...(input === undefined\n\t\t\t? {}\n\t\t\t: { input, stdio: ['pipe', 'pipe', 'pipe'] as const }),\n\t}).trim();\n\n/** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */\nexport function roleName(project: string, stage: string): string {\n\treturn `${project}-github-${stage}`\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9+=,.@_-]/g, '-')\n\t\t.slice(0, 64);\n}\n\n/**\n * Who may assume the role: a workflow job running in this repository's\n * `<stage>` environment, and nothing else — not another branch's job, not\n * another environment's. That is what keeps a staging deploy from using the\n * production role.\n */\nexport function trustPolicy(\n\tproviderArn: string,\n\trepo: string,\n\tstage: string,\n): string {\n\treturn JSON.stringify({\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tPrincipal: { Federated: providerArn },\n\t\t\t\tAction: 'sts:AssumeRoleWithWebIdentity',\n\t\t\t\tCondition: {\n\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:sub`]: `repo:${repo}:environment:${stage}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/** GitHub's OIDC provider in this account, created if it is not there. */\nexport async function ensureOidcProvider(iam: IAMClient): Promise<string> {\n\tconst { OpenIDConnectProviderList = [] } = await iam.send(\n\t\tnew ListOpenIDConnectProvidersCommand({}),\n\t);\n\tconst existing = OpenIDConnectProviderList.find((p) =>\n\t\tp.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`),\n\t)?.Arn;\n\tif (existing) return existing;\n\n\tconst created = await iam.send(\n\t\tnew CreateOpenIDConnectProviderCommand({\n\t\t\tUrl: `https://${GITHUB_OIDC_HOST}`,\n\t\t\tClientIDList: [AUDIENCE],\n\t\t}),\n\t);\n\treturn created.OpenIDConnectProviderArn!;\n}\n\n/** The deploy role, created or brought back to this trust and policy. */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\tpolicyArn: string,\n): Promise<string> {\n\tlet arn: string;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tawait iam.send(\n\t\t\tnew UpdateAssumeRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyDocument: trust,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name !== 'NoSuchEntityException') {\n\t\t\tthrow error;\n\t\t}\n\t\tconst { Role } = await iam.send(\n\t\t\tnew CreateRoleCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tAssumeRolePolicyDocument: trust,\n\t\t\t\tDescription: 'Deploys from GitHub Actions (gkm deploy:github)',\n\t\t\t\t// SST deploys can run long; the default hour is tight.\n\t\t\t\tMaxSessionDuration: 3600 * 2,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t}\n\n\t// Attaching an attached policy is a no-op, so this converges too.\n\tawait iam.send(\n\t\tnew AttachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t);\n\treturn arn;\n}\n\n/**\n * An IAM client for the stage's account.\n *\n * `fromIni` for a named profile, deliberately: it resolves exactly that\n * profile — keys, assume-role, `credential_process` or SSO, whichever\n * ~/.aws/config says — and never falls back to AWS_* in the environment. The\n * provider chain checks the environment first, so exported staging keys would\n * quietly win over `--profile prod` and the production role would land in the\n * staging account.\n */\nexport async function iamFor(\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<IAMClient> {\n\tconst config: IAMClientConfig = { region: 'us-east-1' };\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\tconfig.credentials = { accessKeyId: 'test', secretAccessKey: 'test' };\n\t}\n\treturn new IAMClient(config);\n}\n\nexport async function deployGithubCommand(\n\toptions: DeployGithubOptions,\n\tdeps: { iam?: IAMClient; gh?: Gh; cwd?: string } = {},\n): Promise<{ roleArn?: string; repo: string }> {\n\tconst gh = deps.gh ?? defaultGh;\n\tconst { workspace } = await loadWorkspaceConfig(deps.cwd);\n\tassertDeployedStage(workspace.stages, options.stage);\n\n\tconst repo =\n\t\toptions.repo ??\n\t\tgh(['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner']);\n\tconst role = roleName(workspace.name, options.stage);\n\tconst policyArn = options.policyArn ?? DEFAULT_POLICY_ARN;\n\t// Read through the keystore so a key still at the place keys used to be kept\n\t// is found, and copied to where it is kept now.\n\tconst key = await readKey(options.stage, keystoreProject(workspace));\n\tconst keyPath = getKeyPath(options.stage, keystoreProject(workspace));\n\tconst hasKey = key !== null;\n\t// A stage whose secrets are in a store is read from there by the deploy job,\n\t// with the role; it needs no key on GitHub.\n\tconst remote = isRemoteStore(workspace, options.stage);\n\t// Read from the stage's own store, with the same credentials the role is\n\t// created with: what the deploy job will find there.\n\tconst store = await secretsStoreFor(workspace, options.stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst hasSecrets = remote && (await store.read(options.stage)) !== null;\n\tconst via = options.profile\n\t\t? `profile \"${options.profile}\"`\n\t\t: 'the default credentials';\n\n\tlogger.log(`\\n🔐 GitHub → AWS for stage \"${options.stage}\"\\n`);\n\tlogger.log(` Repository: ${repo}`);\n\tlogger.log(` AWS profile: ${options.profile ?? '(default credentials)'}`);\n\tlogger.log(` Role: ${role}`);\n\tlogger.log(\n\t\t` Policy: ${policyArn}${options.policyArn ? '' : ' (default; --policy-arn to narrow)'}`,\n\t);\n\tlogger.log(` Trusted by: repo:${repo}:environment:${options.stage} only`);\n\tif (remote) {\n\t\tlogger.log(\n\t\t\t` Secrets: ${hasSecrets ? `in the store (${store.name}), read with ${via}` : `none in the store yet — gkm secrets:set <KEY> '…' --stage ${options.stage}`}`,\n\t\t);\n\t} else {\n\t\tlogger.log(\n\t\t\t` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`,\n\t\t);\n\t}\n\n\tif (options.dryRun) {\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo };\n\t}\n\n\tconst iam = deps.iam ?? (await iamFor(options.profile));\n\tconst providerArn = await ensureOidcProvider(iam).catch((error) => {\n\t\t// The first AWS call is where an expired SSO login surfaces; the SDK's\n\t\t// message does not say which profile, and this command knows.\n\t\tif (/SSO session/i.test(String(error?.message)) && options.profile) {\n\t\t\tthrow new SsoSessionExpired(options.profile);\n\t\t}\n\t\tthrow error;\n\t});\n\tconst roleArn = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, repo, options.stage),\n\t\tpolicyArn,\n\t);\n\tlogger.log(`\\n ✓ AWS: ${roleArn}`);\n\n\t// The environment the generated deploy workflow runs the stage in.\n\tgh(['api', '--method', 'PUT', `repos/${repo}/environments/${options.stage}`]);\n\tgh([\n\t\t'variable',\n\t\t'set',\n\t\t'AWS_ROLE_ARN',\n\t\t'--env',\n\t\toptions.stage,\n\t\t'--repo',\n\t\trepo,\n\t\t'--body',\n\t\troleArn,\n\t]);\n\tlogger.log(` ✓ GitHub: environment \"${options.stage}\", AWS_ROLE_ARN`);\n\n\tif (remote) {\n\t\t// Nothing to send: the deploy job reads the stage's store itself.\n\t\tif (!hasSecrets) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ The \"${options.stage}\" store holds no secrets yet; set them with gkm secrets:set <KEY> '…' --stage ${options.stage}${options.profile ? ` (AWS_PROFILE=${options.profile})` : ''}.`,\n\t\t\t);\n\t\t}\n\t} else if (hasKey) {\n\t\t// Through stdin, so the key is never an argument a process list shows.\n\t\tgh(\n\t\t\t[\n\t\t\t\t'secret',\n\t\t\t\t'set',\n\t\t\t\t'GKM_SECRETS_KEY',\n\t\t\t\t'--env',\n\t\t\t\toptions.stage,\n\t\t\t\t'--repo',\n\t\t\t\trepo,\n\t\t\t],\n\t\t\tkey!,\n\t\t);\n\t\tlogger.log(' ✓ GitHub: GKM_SECRETS_KEY');\n\t} else {\n\t\tlogger.log(\n\t\t\t` ⚠ No secrets key for \"${options.stage}\"; run gkm secrets:init --stage ${options.stage}, then this again.`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t`\\n Protect it: add required reviewers to the \"${options.stage}\" environment in the repository settings.\\n`,\n\t);\n\treturn { roleArn, repo };\n}\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends Error {\n\tconstructor(readonly profile: string) {\n\t\tsuper(\n\t\t\t`The SSO session for profile \"${profile}\" has expired or is invalid. Run: aws sso login --profile ${profile}`,\n\t\t);\n\t\tthis.name = 'SsoSessionExpired';\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAM,SAAS;AAEf,MAAM,mBAAmB;AACzB,MAAM,WAAW;AAsBjB,MAAM,aAAiB,MAAM,WAAA,GAC5BA,mBAAAA,aAAAA,CAAa,MAAM,MAAM;CACxB,UAAU;CACV,GAAI,UAAU,KAAA,IACX,CAAC,IACD;EAAE;EAAO,OAAO;GAAC;GAAQ;GAAQ;EAAM;CAAW;AACtD,CAAC,CAAC,CAAC,KAAK;;AAGT,SAAgB,SAAS,SAAiB,OAAuB;CAChE,OAAO,GAAG,QAAQ,UAAU,QAC1B,YAAY,CAAC,CACb,QAAQ,qBAAqB,GAAG,CAAC,CACjC,MAAM,GAAG,EAAE;AACd;;;;;;;AAQA,SAAgB,YACf,aACA,MACA,OACS;CACT,OAAO,KAAK,UAAU;EACrB,SAAS;EACT,WAAW,CACV;GACC,QAAQ;GACR,WAAW,EAAE,WAAW,YAAY;GACpC,QAAQ;GACR,WAAW,EACV,cAAc;KACZ,GAAG,iBAAiB,QAAQ;KAC5B,GAAG,iBAAiB,QAAQ,QAAQ,KAAK,eAAe;GAC1D,EACD;EACD,CACD;CACD,CAAC;AACF;;AAGA,eAAsB,mBAAmB,KAAiC;CACzE,MAAM,EAAE,4BAA4B,CAAC,MAAM,MAAM,IAAI,KACpD,IAAIC,oBAAAA,kCAAkC,CAAC,CAAC,CACzC;CACA,MAAM,WAAW,0BAA0B,MAAM,MAChD,EAAE,KAAK,SAAS,iBAAiB,kBAAkB,CACpD,CAAC,EAAE;CACH,IAAI,UAAU,OAAO;CAQrB,QAAO,MANe,IAAI,KACzB,IAAIC,oBAAAA,mCAAmC;EACtC,KAAK,WAAW;EAChB,cAAc,CAAC,QAAQ;CACxB,CAAC,CACF,EAAA,CACe;AAChB;;AAGA,eAAsB,WACrB,KACA,MACA,OACA,WACkB;CAClB,IAAI;CACJ,IAAI;EACH,MAAM,EAAE,SAAS,MAAM,IAAI,KAAK,IAAIC,oBAAAA,eAAe,EAAE,UAAU,KAAK,CAAC,CAAC;EACtE,MAAM,IAAI,KACT,IAAIC,oBAAAA,8BAA8B;GACjC,UAAU;GACV,gBAAgB;EACjB,CAAC,CACF;EACA,MAAM,KAAM;CACb,SAAS,OAAO;EACf,IAAK,MAA4B,SAAS,yBACzC,MAAM;EAEP,MAAM,EAAE,SAAS,MAAM,IAAI,KAC1B,IAAIC,oBAAAA,kBAAkB;GACrB,UAAU;GACV,0BAA0B;GAC1B,aAAa;GAEb,oBAAoB;EACrB,CAAC,CACF;EACA,MAAM,KAAM;CACb;CAGA,MAAM,IAAI,KACT,IAAIC,oBAAAA,wBAAwB;EAAE,UAAU;EAAM,WAAW;CAAU,CAAC,CACrE;CACA,OAAO;AACR;;;;;;;;;;;AAYA,eAAsB,OACrB,SACA,UACqB;CACrB,MAAM,SAA0B,EAAE,QAAQ,YAAY;CACtD,IAAI,SAAS;EACZ,MAAM,EAAE,YAAY,MAAM,OAAO;EACjC,OAAO,cAAc,QAAQ,EAAE,QAAQ,CAAC;CACzC;CACA,IAAI,UAAU;EACb,OAAO,WAAW;EAClB,OAAO,cAAc;GAAE,aAAa;GAAQ,iBAAiB;EAAO;CACrE;CACA,OAAO,IAAIC,oBAAAA,UAAU,MAAM;AAC5B;AAEA,eAAsB,oBACrB,SACA,OAAmD,CAAC,GACN;CAC9C,MAAM,KAAK,KAAK,MAAM;CACtB,MAAM,EAAE,cAAc,MAAMC,eAAAA,oBAAoB,KAAK,GAAG;CACxD,kBAAA,oBAAoB,UAAU,QAAQ,QAAQ,KAAK;CAEnD,MAAM,OACL,QAAQ,QACR,GAAG;EAAC;EAAQ;EAAQ;EAAU;EAAiB;EAAM;CAAgB,CAAC;CACvE,MAAM,OAAO,SAAS,UAAU,MAAM,QAAQ,KAAK;CACnD,MAAM,YAAY,QAAQ,aAAA;CAG1B,MAAM,MAAM,MAAMC,cAAAA,QAAQ,QAAQ,OAAOC,cAAAA,gBAAgB,SAAS,CAAC;CACnE,MAAM,UAAUC,cAAAA,WAAW,QAAQ,OAAOD,cAAAA,gBAAgB,SAAS,CAAC;CACpE,MAAM,SAAS,QAAQ;CAGvB,MAAM,SAASE,cAAAA,cAAc,WAAW,QAAQ,KAAK;CAGrD,MAAM,QAAQ,MAAMC,cAAAA,gBAAgB,WAAW,QAAQ,OAAO,EAC7D,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC,EACvD,CAAC;CACD,MAAM,aAAa,UAAW,MAAM,MAAM,KAAK,QAAQ,KAAK,MAAO;CACnE,MAAM,MAAM,QAAQ,UACjB,YAAY,QAAQ,QAAQ,KAC5B;CAEH,OAAO,IAAI,gCAAgC,QAAQ,MAAM,IAAI;CAC7D,OAAO,IAAI,mBAAmB,MAAM;CACpC,OAAO,IAAI,mBAAmB,QAAQ,WAAW,yBAAyB;CAC1E,OAAO,IAAI,mBAAmB,MAAM;CACpC,OAAO,IACN,mBAAmB,YAAY,QAAQ,YAAY,KAAK,uCACzD;CACA,OAAO,IAAI,wBAAwB,KAAK,eAAe,QAAQ,MAAM,MAAM;CAC3E,IAAI,QACH,OAAO,IACN,mBAAmB,aAAa,iBAAiB,MAAM,KAAK,eAAe,QAAQ,6DAA6D,QAAQ,SACzJ;MAEA,OAAO,IACN,mBAAmB,SAAS,UAAU,WAAW,QAAQ,kCAAkC,QAAQ,MAAM,SAC1G;CAGD,IAAI,QAAQ,QAAQ;EACnB,OAAO,IAAI,mCAAmC;EAC9C,OAAO,EAAE,KAAK;CACf;CAEA,MAAM,MAAM,KAAK,OAAQ,MAAM,OAAO,QAAQ,OAAO;CASrD,MAAM,UAAU,MAAM,WACrB,KACA,MACA,YAAY,MAXa,mBAAmB,GAAG,CAAC,CAAC,OAAO,UAAU;EAGlE,IAAI,eAAe,KAAK,OAAO,OAAO,OAAO,CAAC,KAAK,QAAQ,SAC1D,MAAM,IAAI,kBAAkB,QAAQ,OAAO;EAE5C,MAAM;CACP,CAAC,GAIyB,MAAM,QAAQ,KAAK,GAC5C,SACD;CACA,OAAO,IAAI,cAAc,SAAS;CAGlC,GAAG;EAAC;EAAO;EAAY;EAAO,SAAS,KAAK,gBAAgB,QAAQ;CAAO,CAAC;CAC5E,GAAG;EACF;EACA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;EACA;CACD,CAAC;CACD,OAAO,IAAI,4BAA4B,QAAQ,MAAM,gBAAgB;CAErE,IAAI,QAEC;MAAA,CAAC,YACJ,OAAO,IACN,YAAY,QAAQ,MAAM,gFAAgF,QAAQ,QAAQ,QAAQ,UAAU,iBAAiB,QAAQ,QAAQ,KAAK,GAAG,EACtL;CAAA,OAEK,IAAI,QAAQ;EAElB,GACC;GACC;GACA;GACA;GACA;GACA,QAAQ;GACR;GACA;EACD,GACA,GACD;EACA,OAAO,IAAI,6BAA6B;CACzC,OACC,OAAO,IACN,2BAA2B,QAAQ,MAAM,kCAAkC,QAAQ,MAAM,mBAC1F;CAGD,OAAO,IACN,kDAAkD,QAAQ,MAAM,4CACjE;CACA,OAAO;EAAE;EAAS;CAAK;AACxB;;AAGA,IAAa,oBAAb,cAAuC,MAAM;CACvB;CAArB,YAAY,SAA0B;EACrC,MACC,gCAAgC,QAAQ,4DAA4D,SACrG;EAHoB,KAAA,UAAA;EAIpB,KAAK,OAAO;CACb;AACD"}
1
+ {"version":3,"file":"github-BvN3CfxM.cjs","names":["execFileSync","ListOpenIDConnectProvidersCommand","CreateOpenIDConnectProviderCommand","GetRoleCommand","UpdateAssumeRolePolicyCommand","CreateRoleCommand","AttachRolePolicyCommand","IAMClient","loadWorkspaceConfig","readKey","keystoreProject","getKeyPath","isRemoteStore","secretsStoreFor"],"sources":["../src/deploy/github.ts"],"sourcesContent":["/**\n * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.\n *\n * A stage usually lives in its own AWS account — staging in one, production in\n * another — so this runs once per stage, with that account's profile:\n *\n * gkm deploy:github --stage staging --profile acme-dev\n * gkm deploy:github --stage prod --profile acme-prod\n *\n * In the stage's account it makes sure GitHub's OIDC provider exists, and a\n * role that only this repository's `<stage>` environment can assume. On\n * GitHub it creates that environment and gives it what the generated deploy\n * workflow reads: `AWS_ROLE_ARN`, and — when the stage's secrets are kept in\n * the local file — its key as `GKM_SECRETS_KEY`. With `secrets.store` set to\n * SSM the deploy job pulls them with the role instead, so no key is handed to\n * GitHub, and this pushes the stage's local secrets to SSM in the same account,\n * with the same profile. Re-running it converges rather than duplicating.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListOpenIDConnectProvidersCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { getKeyPath, keystoreProject, readKey } from '../secrets/keystore.js';\nimport { isRemoteStore, secretsStoreFor } from '../secrets/store.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default —\n * said out loud in the output, and replaceable with `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\nexport interface DeployGithubOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account. */\n\tprofile?: string;\n\t/** `owner/name`; defaults to the repository `gh` sees here. */\n\trepo?: string;\n\t/** The policy the deploy role gets. */\n\tpolicyArn?: string;\n\tdryRun?: boolean;\n}\n\n/** Runs `gh` with the given arguments and optional stdin, returning stdout. */\nexport type Gh = (args: string[], input?: string) => string;\n\nconst defaultGh: Gh = (args, input) =>\n\texecFileSync('gh', args, {\n\t\tencoding: 'utf-8',\n\t\t...(input === undefined\n\t\t\t? {}\n\t\t\t: { input, stdio: ['pipe', 'pipe', 'pipe'] as const }),\n\t}).trim();\n\n/** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */\nexport function roleName(project: string, stage: string): string {\n\treturn `${project}-github-${stage}`\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9+=,.@_-]/g, '-')\n\t\t.slice(0, 64);\n}\n\n/**\n * Who may assume the role: a workflow job running in this repository's\n * `<stage>` environment, and nothing else — not another branch's job, not\n * another environment's. That is what keeps a staging deploy from using the\n * production role.\n */\nexport function trustPolicy(\n\tproviderArn: string,\n\trepo: string,\n\tstage: string,\n): string {\n\treturn JSON.stringify({\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tPrincipal: { Federated: providerArn },\n\t\t\t\tAction: 'sts:AssumeRoleWithWebIdentity',\n\t\t\t\tCondition: {\n\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:sub`]: `repo:${repo}:environment:${stage}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/** GitHub's OIDC provider in this account, created if it is not there. */\nexport async function ensureOidcProvider(iam: IAMClient): Promise<string> {\n\tconst { OpenIDConnectProviderList = [] } = await iam.send(\n\t\tnew ListOpenIDConnectProvidersCommand({}),\n\t);\n\tconst existing = OpenIDConnectProviderList.find((p) =>\n\t\tp.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`),\n\t)?.Arn;\n\tif (existing) return existing;\n\n\tconst created = await iam.send(\n\t\tnew CreateOpenIDConnectProviderCommand({\n\t\t\tUrl: `https://${GITHUB_OIDC_HOST}`,\n\t\t\tClientIDList: [AUDIENCE],\n\t\t}),\n\t);\n\treturn created.OpenIDConnectProviderArn!;\n}\n\n/** The deploy role, created or brought back to this trust and policy. */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\tpolicyArn: string,\n): Promise<string> {\n\tlet arn: string;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tawait iam.send(\n\t\t\tnew UpdateAssumeRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyDocument: trust,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name !== 'NoSuchEntityException') {\n\t\t\tthrow error;\n\t\t}\n\t\tconst { Role } = await iam.send(\n\t\t\tnew CreateRoleCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tAssumeRolePolicyDocument: trust,\n\t\t\t\tDescription: 'Deploys from GitHub Actions (gkm deploy:github)',\n\t\t\t\t// SST deploys can run long; the default hour is tight.\n\t\t\t\tMaxSessionDuration: 3600 * 2,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t}\n\n\t// Attaching an attached policy is a no-op, so this converges too.\n\tawait iam.send(\n\t\tnew AttachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t);\n\treturn arn;\n}\n\n/**\n * An IAM client for the stage's account.\n *\n * `fromIni` for a named profile, deliberately: it resolves exactly that\n * profile — keys, assume-role, `credential_process` or SSO, whichever\n * ~/.aws/config says — and never falls back to AWS_* in the environment. The\n * provider chain checks the environment first, so exported staging keys would\n * quietly win over `--profile prod` and the production role would land in the\n * staging account.\n */\nexport async function iamFor(\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<IAMClient> {\n\tconst config: IAMClientConfig = { region: 'us-east-1' };\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\tconfig.credentials = { accessKeyId: 'test', secretAccessKey: 'test' };\n\t}\n\treturn new IAMClient(config);\n}\n\nexport async function deployGithubCommand(\n\toptions: DeployGithubOptions,\n\tdeps: { iam?: IAMClient; gh?: Gh; cwd?: string } = {},\n): Promise<{ roleArn?: string; repo: string }> {\n\tconst gh = deps.gh ?? defaultGh;\n\tconst { workspace } = await loadWorkspaceConfig(deps.cwd);\n\tassertDeployedStage(workspace.stages, options.stage);\n\n\tconst repo =\n\t\toptions.repo ??\n\t\tgh(['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner']);\n\tconst role = roleName(workspace.name, options.stage);\n\tconst policyArn = options.policyArn ?? DEFAULT_POLICY_ARN;\n\t// Read through the keystore so a key still at the place keys used to be kept\n\t// is found, and copied to where it is kept now.\n\tconst key = await readKey(options.stage, keystoreProject(workspace));\n\tconst keyPath = getKeyPath(options.stage, keystoreProject(workspace));\n\tconst hasKey = key !== null;\n\t// A stage whose secrets are in a store is read from there by the deploy job,\n\t// with the role; it needs no key on GitHub.\n\tconst remote = isRemoteStore(workspace, options.stage);\n\t// Read from the stage's own store, with the same credentials the role is\n\t// created with: what the deploy job will find there.\n\tconst store = await secretsStoreFor(workspace, options.stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst hasSecrets = remote && (await store.read(options.stage)) !== null;\n\tconst via = options.profile\n\t\t? `profile \"${options.profile}\"`\n\t\t: 'the default credentials';\n\n\tlogger.log(`\\n🔐 GitHub → AWS for stage \"${options.stage}\"\\n`);\n\tlogger.log(` Repository: ${repo}`);\n\tlogger.log(` AWS profile: ${options.profile ?? '(default credentials)'}`);\n\tlogger.log(` Role: ${role}`);\n\tlogger.log(\n\t\t` Policy: ${policyArn}${options.policyArn ? '' : ' (default; --policy-arn to narrow)'}`,\n\t);\n\tlogger.log(` Trusted by: repo:${repo}:environment:${options.stage} only`);\n\tif (remote) {\n\t\tlogger.log(\n\t\t\t` Secrets: ${hasSecrets ? `in the store (${store.name}), read with ${via}` : `none in the store yet — gkm secrets:set <KEY> '…' --stage ${options.stage}`}`,\n\t\t);\n\t} else {\n\t\tlogger.log(\n\t\t\t` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`,\n\t\t);\n\t}\n\n\tif (options.dryRun) {\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo };\n\t}\n\n\tconst iam = deps.iam ?? (await iamFor(options.profile));\n\tconst providerArn = await ensureOidcProvider(iam).catch((error) => {\n\t\t// The first AWS call is where an expired SSO login surfaces; the SDK's\n\t\t// message does not say which profile, and this command knows.\n\t\tif (/SSO session/i.test(String(error?.message)) && options.profile) {\n\t\t\tthrow new SsoSessionExpired(options.profile);\n\t\t}\n\t\tthrow error;\n\t});\n\tconst roleArn = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, repo, options.stage),\n\t\tpolicyArn,\n\t);\n\tlogger.log(`\\n ✓ AWS: ${roleArn}`);\n\n\t// The environment the generated deploy workflow runs the stage in.\n\tgh(['api', '--method', 'PUT', `repos/${repo}/environments/${options.stage}`]);\n\tgh([\n\t\t'variable',\n\t\t'set',\n\t\t'AWS_ROLE_ARN',\n\t\t'--env',\n\t\toptions.stage,\n\t\t'--repo',\n\t\trepo,\n\t\t'--body',\n\t\troleArn,\n\t]);\n\tlogger.log(` ✓ GitHub: environment \"${options.stage}\", AWS_ROLE_ARN`);\n\n\tif (remote) {\n\t\t// Nothing to send: the deploy job reads the stage's store itself.\n\t\tif (!hasSecrets) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ The \"${options.stage}\" store holds no secrets yet; set them with gkm secrets:set <KEY> '…' --stage ${options.stage}${options.profile ? ` (AWS_PROFILE=${options.profile})` : ''}.`,\n\t\t\t);\n\t\t}\n\t} else if (hasKey) {\n\t\t// Through stdin, so the key is never an argument a process list shows.\n\t\tgh(\n\t\t\t[\n\t\t\t\t'secret',\n\t\t\t\t'set',\n\t\t\t\t'GKM_SECRETS_KEY',\n\t\t\t\t'--env',\n\t\t\t\toptions.stage,\n\t\t\t\t'--repo',\n\t\t\t\trepo,\n\t\t\t],\n\t\t\tkey!,\n\t\t);\n\t\tlogger.log(' ✓ GitHub: GKM_SECRETS_KEY');\n\t} else {\n\t\tlogger.log(\n\t\t\t` ⚠ No secrets key for \"${options.stage}\"; run gkm secrets:init --stage ${options.stage}, then this again.`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t`\\n Protect it: add required reviewers to the \"${options.stage}\" environment in the repository settings.\\n`,\n\t);\n\treturn { roleArn, repo };\n}\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends Error {\n\tconstructor(readonly profile: string) {\n\t\tsuper(\n\t\t\t`The SSO session for profile \"${profile}\" has expired or is invalid. Run: aws sso login --profile ${profile}`,\n\t\t);\n\t\tthis.name = 'SsoSessionExpired';\n\t}\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { k as assertDeployedStage } from "./workspace-C7NqCsQF.mjs";
2
2
  import { loadWorkspaceConfig } from "./config.mjs";
3
- import { Gt as isRemoteStore, Kt as secretsStoreFor, Qt as readKey, Xt as keystoreProject, Yt as getKeyPath } from "./state-DtS2HEfi.mjs";
3
+ import { Gt as isRemoteStore, Kt as secretsStoreFor, Qt as readKey, Xt as keystoreProject, Yt as getKeyPath } from "./state-DpkxSwzI.mjs";
4
4
  import { execFileSync } from "node:child_process";
5
5
  import { AttachRolePolicyCommand, CreateOpenIDConnectProviderCommand, CreateRoleCommand, GetRoleCommand, IAMClient, ListOpenIDConnectProvidersCommand, UpdateAssumeRolePolicyCommand } from "@aws-sdk/client-iam";
6
6
  //#region src/deploy/github.ts
@@ -210,4 +210,4 @@ var SsoSessionExpired = class extends Error {
210
210
  //#endregion
211
211
  export { deployGithubCommand };
212
212
 
213
- //# sourceMappingURL=github-hjHHm7jM.mjs.map
213
+ //# sourceMappingURL=github-C9ODoyZU.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"github-hjHHm7jM.mjs","names":[],"sources":["../src/deploy/github.ts"],"sourcesContent":["/**\n * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.\n *\n * A stage usually lives in its own AWS account — staging in one, production in\n * another — so this runs once per stage, with that account's profile:\n *\n * gkm deploy:github --stage staging --profile acme-dev\n * gkm deploy:github --stage prod --profile acme-prod\n *\n * In the stage's account it makes sure GitHub's OIDC provider exists, and a\n * role that only this repository's `<stage>` environment can assume. On\n * GitHub it creates that environment and gives it what the generated deploy\n * workflow reads: `AWS_ROLE_ARN`, and — when the stage's secrets are kept in\n * the local file — its key as `GKM_SECRETS_KEY`. With `secrets.store` set to\n * SSM the deploy job pulls them with the role instead, so no key is handed to\n * GitHub, and this pushes the stage's local secrets to SSM in the same account,\n * with the same profile. Re-running it converges rather than duplicating.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListOpenIDConnectProvidersCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { getKeyPath, keystoreProject, readKey } from '../secrets/keystore.js';\nimport { isRemoteStore, secretsStoreFor } from '../secrets/store.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default —\n * said out loud in the output, and replaceable with `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\nexport interface DeployGithubOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account. */\n\tprofile?: string;\n\t/** `owner/name`; defaults to the repository `gh` sees here. */\n\trepo?: string;\n\t/** The policy the deploy role gets. */\n\tpolicyArn?: string;\n\tdryRun?: boolean;\n}\n\n/** Runs `gh` with the given arguments and optional stdin, returning stdout. */\nexport type Gh = (args: string[], input?: string) => string;\n\nconst defaultGh: Gh = (args, input) =>\n\texecFileSync('gh', args, {\n\t\tencoding: 'utf-8',\n\t\t...(input === undefined\n\t\t\t? {}\n\t\t\t: { input, stdio: ['pipe', 'pipe', 'pipe'] as const }),\n\t}).trim();\n\n/** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */\nexport function roleName(project: string, stage: string): string {\n\treturn `${project}-github-${stage}`\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9+=,.@_-]/g, '-')\n\t\t.slice(0, 64);\n}\n\n/**\n * Who may assume the role: a workflow job running in this repository's\n * `<stage>` environment, and nothing else — not another branch's job, not\n * another environment's. That is what keeps a staging deploy from using the\n * production role.\n */\nexport function trustPolicy(\n\tproviderArn: string,\n\trepo: string,\n\tstage: string,\n): string {\n\treturn JSON.stringify({\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tPrincipal: { Federated: providerArn },\n\t\t\t\tAction: 'sts:AssumeRoleWithWebIdentity',\n\t\t\t\tCondition: {\n\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:sub`]: `repo:${repo}:environment:${stage}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/** GitHub's OIDC provider in this account, created if it is not there. */\nexport async function ensureOidcProvider(iam: IAMClient): Promise<string> {\n\tconst { OpenIDConnectProviderList = [] } = await iam.send(\n\t\tnew ListOpenIDConnectProvidersCommand({}),\n\t);\n\tconst existing = OpenIDConnectProviderList.find((p) =>\n\t\tp.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`),\n\t)?.Arn;\n\tif (existing) return existing;\n\n\tconst created = await iam.send(\n\t\tnew CreateOpenIDConnectProviderCommand({\n\t\t\tUrl: `https://${GITHUB_OIDC_HOST}`,\n\t\t\tClientIDList: [AUDIENCE],\n\t\t}),\n\t);\n\treturn created.OpenIDConnectProviderArn!;\n}\n\n/** The deploy role, created or brought back to this trust and policy. */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\tpolicyArn: string,\n): Promise<string> {\n\tlet arn: string;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tawait iam.send(\n\t\t\tnew UpdateAssumeRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyDocument: trust,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name !== 'NoSuchEntityException') {\n\t\t\tthrow error;\n\t\t}\n\t\tconst { Role } = await iam.send(\n\t\t\tnew CreateRoleCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tAssumeRolePolicyDocument: trust,\n\t\t\t\tDescription: 'Deploys from GitHub Actions (gkm deploy:github)',\n\t\t\t\t// SST deploys can run long; the default hour is tight.\n\t\t\t\tMaxSessionDuration: 3600 * 2,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t}\n\n\t// Attaching an attached policy is a no-op, so this converges too.\n\tawait iam.send(\n\t\tnew AttachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t);\n\treturn arn;\n}\n\n/**\n * An IAM client for the stage's account.\n *\n * `fromIni` for a named profile, deliberately: it resolves exactly that\n * profile — keys, assume-role, `credential_process` or SSO, whichever\n * ~/.aws/config says — and never falls back to AWS_* in the environment. The\n * provider chain checks the environment first, so exported staging keys would\n * quietly win over `--profile prod` and the production role would land in the\n * staging account.\n */\nexport async function iamFor(\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<IAMClient> {\n\tconst config: IAMClientConfig = { region: 'us-east-1' };\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\tconfig.credentials = { accessKeyId: 'test', secretAccessKey: 'test' };\n\t}\n\treturn new IAMClient(config);\n}\n\nexport async function deployGithubCommand(\n\toptions: DeployGithubOptions,\n\tdeps: { iam?: IAMClient; gh?: Gh; cwd?: string } = {},\n): Promise<{ roleArn?: string; repo: string }> {\n\tconst gh = deps.gh ?? defaultGh;\n\tconst { workspace } = await loadWorkspaceConfig(deps.cwd);\n\tassertDeployedStage(workspace.stages, options.stage);\n\n\tconst repo =\n\t\toptions.repo ??\n\t\tgh(['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner']);\n\tconst role = roleName(workspace.name, options.stage);\n\tconst policyArn = options.policyArn ?? DEFAULT_POLICY_ARN;\n\t// Read through the keystore so a key still at the place keys used to be kept\n\t// is found, and copied to where it is kept now.\n\tconst key = await readKey(options.stage, keystoreProject(workspace));\n\tconst keyPath = getKeyPath(options.stage, keystoreProject(workspace));\n\tconst hasKey = key !== null;\n\t// A stage whose secrets are in a store is read from there by the deploy job,\n\t// with the role; it needs no key on GitHub.\n\tconst remote = isRemoteStore(workspace, options.stage);\n\t// Read from the stage's own store, with the same credentials the role is\n\t// created with: what the deploy job will find there.\n\tconst store = await secretsStoreFor(workspace, options.stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst hasSecrets = remote && (await store.read(options.stage)) !== null;\n\tconst via = options.profile\n\t\t? `profile \"${options.profile}\"`\n\t\t: 'the default credentials';\n\n\tlogger.log(`\\n🔐 GitHub → AWS for stage \"${options.stage}\"\\n`);\n\tlogger.log(` Repository: ${repo}`);\n\tlogger.log(` AWS profile: ${options.profile ?? '(default credentials)'}`);\n\tlogger.log(` Role: ${role}`);\n\tlogger.log(\n\t\t` Policy: ${policyArn}${options.policyArn ? '' : ' (default; --policy-arn to narrow)'}`,\n\t);\n\tlogger.log(` Trusted by: repo:${repo}:environment:${options.stage} only`);\n\tif (remote) {\n\t\tlogger.log(\n\t\t\t` Secrets: ${hasSecrets ? `in the store (${store.name}), read with ${via}` : `none in the store yet — gkm secrets:set <KEY> '…' --stage ${options.stage}`}`,\n\t\t);\n\t} else {\n\t\tlogger.log(\n\t\t\t` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`,\n\t\t);\n\t}\n\n\tif (options.dryRun) {\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo };\n\t}\n\n\tconst iam = deps.iam ?? (await iamFor(options.profile));\n\tconst providerArn = await ensureOidcProvider(iam).catch((error) => {\n\t\t// The first AWS call is where an expired SSO login surfaces; the SDK's\n\t\t// message does not say which profile, and this command knows.\n\t\tif (/SSO session/i.test(String(error?.message)) && options.profile) {\n\t\t\tthrow new SsoSessionExpired(options.profile);\n\t\t}\n\t\tthrow error;\n\t});\n\tconst roleArn = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, repo, options.stage),\n\t\tpolicyArn,\n\t);\n\tlogger.log(`\\n ✓ AWS: ${roleArn}`);\n\n\t// The environment the generated deploy workflow runs the stage in.\n\tgh(['api', '--method', 'PUT', `repos/${repo}/environments/${options.stage}`]);\n\tgh([\n\t\t'variable',\n\t\t'set',\n\t\t'AWS_ROLE_ARN',\n\t\t'--env',\n\t\toptions.stage,\n\t\t'--repo',\n\t\trepo,\n\t\t'--body',\n\t\troleArn,\n\t]);\n\tlogger.log(` ✓ GitHub: environment \"${options.stage}\", AWS_ROLE_ARN`);\n\n\tif (remote) {\n\t\t// Nothing to send: the deploy job reads the stage's store itself.\n\t\tif (!hasSecrets) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ The \"${options.stage}\" store holds no secrets yet; set them with gkm secrets:set <KEY> '…' --stage ${options.stage}${options.profile ? ` (AWS_PROFILE=${options.profile})` : ''}.`,\n\t\t\t);\n\t\t}\n\t} else if (hasKey) {\n\t\t// Through stdin, so the key is never an argument a process list shows.\n\t\tgh(\n\t\t\t[\n\t\t\t\t'secret',\n\t\t\t\t'set',\n\t\t\t\t'GKM_SECRETS_KEY',\n\t\t\t\t'--env',\n\t\t\t\toptions.stage,\n\t\t\t\t'--repo',\n\t\t\t\trepo,\n\t\t\t],\n\t\t\tkey!,\n\t\t);\n\t\tlogger.log(' ✓ GitHub: GKM_SECRETS_KEY');\n\t} else {\n\t\tlogger.log(\n\t\t\t` ⚠ No secrets key for \"${options.stage}\"; run gkm secrets:init --stage ${options.stage}, then this again.`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t`\\n Protect it: add required reviewers to the \"${options.stage}\" environment in the repository settings.\\n`,\n\t);\n\treturn { roleArn, repo };\n}\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends Error {\n\tconstructor(readonly profile: string) {\n\t\tsuper(\n\t\t\t`The SSO session for profile \"${profile}\" has expired or is invalid. Run: aws sso login --profile ${profile}`,\n\t\t);\n\t\tthis.name = 'SsoSessionExpired';\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"github-C9ODoyZU.mjs","names":[],"sources":["../src/deploy/github.ts"],"sourcesContent":["/**\n * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.\n *\n * A stage usually lives in its own AWS account — staging in one, production in\n * another — so this runs once per stage, with that account's profile:\n *\n * gkm deploy:github --stage staging --profile acme-dev\n * gkm deploy:github --stage prod --profile acme-prod\n *\n * In the stage's account it makes sure GitHub's OIDC provider exists, and a\n * role that only this repository's `<stage>` environment can assume. On\n * GitHub it creates that environment and gives it what the generated deploy\n * workflow reads: `AWS_ROLE_ARN`, and — when the stage's secrets are kept in\n * the local file — its key as `GKM_SECRETS_KEY`. With `secrets.store` set to\n * SSM the deploy job pulls them with the role instead, so no key is handed to\n * GitHub, and this pushes the stage's local secrets to SSM in the same account,\n * with the same profile. Re-running it converges rather than duplicating.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListOpenIDConnectProvidersCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { getKeyPath, keystoreProject, readKey } from '../secrets/keystore.js';\nimport { isRemoteStore, secretsStoreFor } from '../secrets/store.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default —\n * said out loud in the output, and replaceable with `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\nexport interface DeployGithubOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account. */\n\tprofile?: string;\n\t/** `owner/name`; defaults to the repository `gh` sees here. */\n\trepo?: string;\n\t/** The policy the deploy role gets. */\n\tpolicyArn?: string;\n\tdryRun?: boolean;\n}\n\n/** Runs `gh` with the given arguments and optional stdin, returning stdout. */\nexport type Gh = (args: string[], input?: string) => string;\n\nconst defaultGh: Gh = (args, input) =>\n\texecFileSync('gh', args, {\n\t\tencoding: 'utf-8',\n\t\t...(input === undefined\n\t\t\t? {}\n\t\t\t: { input, stdio: ['pipe', 'pipe', 'pipe'] as const }),\n\t}).trim();\n\n/** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */\nexport function roleName(project: string, stage: string): string {\n\treturn `${project}-github-${stage}`\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9+=,.@_-]/g, '-')\n\t\t.slice(0, 64);\n}\n\n/**\n * Who may assume the role: a workflow job running in this repository's\n * `<stage>` environment, and nothing else — not another branch's job, not\n * another environment's. That is what keeps a staging deploy from using the\n * production role.\n */\nexport function trustPolicy(\n\tproviderArn: string,\n\trepo: string,\n\tstage: string,\n): string {\n\treturn JSON.stringify({\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tPrincipal: { Federated: providerArn },\n\t\t\t\tAction: 'sts:AssumeRoleWithWebIdentity',\n\t\t\t\tCondition: {\n\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:sub`]: `repo:${repo}:environment:${stage}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/** GitHub's OIDC provider in this account, created if it is not there. */\nexport async function ensureOidcProvider(iam: IAMClient): Promise<string> {\n\tconst { OpenIDConnectProviderList = [] } = await iam.send(\n\t\tnew ListOpenIDConnectProvidersCommand({}),\n\t);\n\tconst existing = OpenIDConnectProviderList.find((p) =>\n\t\tp.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`),\n\t)?.Arn;\n\tif (existing) return existing;\n\n\tconst created = await iam.send(\n\t\tnew CreateOpenIDConnectProviderCommand({\n\t\t\tUrl: `https://${GITHUB_OIDC_HOST}`,\n\t\t\tClientIDList: [AUDIENCE],\n\t\t}),\n\t);\n\treturn created.OpenIDConnectProviderArn!;\n}\n\n/** The deploy role, created or brought back to this trust and policy. */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\tpolicyArn: string,\n): Promise<string> {\n\tlet arn: string;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tawait iam.send(\n\t\t\tnew UpdateAssumeRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyDocument: trust,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name !== 'NoSuchEntityException') {\n\t\t\tthrow error;\n\t\t}\n\t\tconst { Role } = await iam.send(\n\t\t\tnew CreateRoleCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tAssumeRolePolicyDocument: trust,\n\t\t\t\tDescription: 'Deploys from GitHub Actions (gkm deploy:github)',\n\t\t\t\t// SST deploys can run long; the default hour is tight.\n\t\t\t\tMaxSessionDuration: 3600 * 2,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t}\n\n\t// Attaching an attached policy is a no-op, so this converges too.\n\tawait iam.send(\n\t\tnew AttachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t);\n\treturn arn;\n}\n\n/**\n * An IAM client for the stage's account.\n *\n * `fromIni` for a named profile, deliberately: it resolves exactly that\n * profile — keys, assume-role, `credential_process` or SSO, whichever\n * ~/.aws/config says — and never falls back to AWS_* in the environment. The\n * provider chain checks the environment first, so exported staging keys would\n * quietly win over `--profile prod` and the production role would land in the\n * staging account.\n */\nexport async function iamFor(\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<IAMClient> {\n\tconst config: IAMClientConfig = { region: 'us-east-1' };\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\tconfig.credentials = { accessKeyId: 'test', secretAccessKey: 'test' };\n\t}\n\treturn new IAMClient(config);\n}\n\nexport async function deployGithubCommand(\n\toptions: DeployGithubOptions,\n\tdeps: { iam?: IAMClient; gh?: Gh; cwd?: string } = {},\n): Promise<{ roleArn?: string; repo: string }> {\n\tconst gh = deps.gh ?? defaultGh;\n\tconst { workspace } = await loadWorkspaceConfig(deps.cwd);\n\tassertDeployedStage(workspace.stages, options.stage);\n\n\tconst repo =\n\t\toptions.repo ??\n\t\tgh(['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner']);\n\tconst role = roleName(workspace.name, options.stage);\n\tconst policyArn = options.policyArn ?? DEFAULT_POLICY_ARN;\n\t// Read through the keystore so a key still at the place keys used to be kept\n\t// is found, and copied to where it is kept now.\n\tconst key = await readKey(options.stage, keystoreProject(workspace));\n\tconst keyPath = getKeyPath(options.stage, keystoreProject(workspace));\n\tconst hasKey = key !== null;\n\t// A stage whose secrets are in a store is read from there by the deploy job,\n\t// with the role; it needs no key on GitHub.\n\tconst remote = isRemoteStore(workspace, options.stage);\n\t// Read from the stage's own store, with the same credentials the role is\n\t// created with: what the deploy job will find there.\n\tconst store = await secretsStoreFor(workspace, options.stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst hasSecrets = remote && (await store.read(options.stage)) !== null;\n\tconst via = options.profile\n\t\t? `profile \"${options.profile}\"`\n\t\t: 'the default credentials';\n\n\tlogger.log(`\\n🔐 GitHub → AWS for stage \"${options.stage}\"\\n`);\n\tlogger.log(` Repository: ${repo}`);\n\tlogger.log(` AWS profile: ${options.profile ?? '(default credentials)'}`);\n\tlogger.log(` Role: ${role}`);\n\tlogger.log(\n\t\t` Policy: ${policyArn}${options.policyArn ? '' : ' (default; --policy-arn to narrow)'}`,\n\t);\n\tlogger.log(` Trusted by: repo:${repo}:environment:${options.stage} only`);\n\tif (remote) {\n\t\tlogger.log(\n\t\t\t` Secrets: ${hasSecrets ? `in the store (${store.name}), read with ${via}` : `none in the store yet — gkm secrets:set <KEY> '…' --stage ${options.stage}`}`,\n\t\t);\n\t} else {\n\t\tlogger.log(\n\t\t\t` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`,\n\t\t);\n\t}\n\n\tif (options.dryRun) {\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo };\n\t}\n\n\tconst iam = deps.iam ?? (await iamFor(options.profile));\n\tconst providerArn = await ensureOidcProvider(iam).catch((error) => {\n\t\t// The first AWS call is where an expired SSO login surfaces; the SDK's\n\t\t// message does not say which profile, and this command knows.\n\t\tif (/SSO session/i.test(String(error?.message)) && options.profile) {\n\t\t\tthrow new SsoSessionExpired(options.profile);\n\t\t}\n\t\tthrow error;\n\t});\n\tconst roleArn = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, repo, options.stage),\n\t\tpolicyArn,\n\t);\n\tlogger.log(`\\n ✓ AWS: ${roleArn}`);\n\n\t// The environment the generated deploy workflow runs the stage in.\n\tgh(['api', '--method', 'PUT', `repos/${repo}/environments/${options.stage}`]);\n\tgh([\n\t\t'variable',\n\t\t'set',\n\t\t'AWS_ROLE_ARN',\n\t\t'--env',\n\t\toptions.stage,\n\t\t'--repo',\n\t\trepo,\n\t\t'--body',\n\t\troleArn,\n\t]);\n\tlogger.log(` ✓ GitHub: environment \"${options.stage}\", AWS_ROLE_ARN`);\n\n\tif (remote) {\n\t\t// Nothing to send: the deploy job reads the stage's store itself.\n\t\tif (!hasSecrets) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ The \"${options.stage}\" store holds no secrets yet; set them with gkm secrets:set <KEY> '…' --stage ${options.stage}${options.profile ? ` (AWS_PROFILE=${options.profile})` : ''}.`,\n\t\t\t);\n\t\t}\n\t} else if (hasKey) {\n\t\t// Through stdin, so the key is never an argument a process list shows.\n\t\tgh(\n\t\t\t[\n\t\t\t\t'secret',\n\t\t\t\t'set',\n\t\t\t\t'GKM_SECRETS_KEY',\n\t\t\t\t'--env',\n\t\t\t\toptions.stage,\n\t\t\t\t'--repo',\n\t\t\t\trepo,\n\t\t\t],\n\t\t\tkey!,\n\t\t);\n\t\tlogger.log(' ✓ GitHub: GKM_SECRETS_KEY');\n\t} else {\n\t\tlogger.log(\n\t\t\t` ⚠ No secrets key for \"${options.stage}\"; run gkm secrets:init --stage ${options.stage}, then this again.`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t`\\n Protect it: add required reviewers to the \"${options.stage}\" environment in the repository settings.\\n`,\n\t);\n\treturn { roleArn, repo };\n}\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends Error {\n\tconstructor(readonly profile: string) {\n\t\tsuper(\n\t\t\t`The SSO session for profile \"${profile}\" has expired or is invalid. Run: aws sso login --profile ${profile}`,\n\t\t);\n\t\tthis.name = 'SsoSessionExpired';\n\t}\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { E as WorkspaceInput, S as NormalizedWorkspace, T as WorkspaceConfig, a as Routes, k as Framework, l as AppsRecord, r as GkmConfig, v as InferredWorkspaceConfig, y as LoadedConfig } from "./types-BLdCL4fB.mjs";
1
+ import { E as WorkspaceInput, S as NormalizedWorkspace, T as WorkspaceConfig, a as Routes, k as Framework, l as AppsRecord, r as GkmConfig, v as InferredWorkspaceConfig, y as LoadedConfig } from "./types-Dp9lgx_S.mjs";
2
2
  //#region src/workspace/publicEnv.d.ts
3
3
  /**
4
4
  * All public env-var prefixes the toolbox understands.
@@ -166,4 +166,4 @@ declare function getEndpointForStage(dokployConfig: {
166
166
  } | undefined, stage: string): string | undefined;
167
167
  //#endregion
168
168
  export { constructPatterns as a, getAppGkmConfig as c, processConfig as d, workspaceConstructGlobs as f, stripPublicPrefix as g, getPublicEnvPrefix as h, assertNoMovedDeployKeys as i, getEndpointForStage as l, PUBLIC_ENV_PREFIXES as m, allConstructGlobs as n, defineWorkspace as o, wrapSingleAppAsWorkspace as p, appConstructGlobs as r, getAppBuildOrder as s, DokployRegistryMoved as t, normalizeWorkspace as u };
169
- //# sourceMappingURL=index-DHyTqGG3.d.mts.map
169
+ //# sourceMappingURL=index-B-1lLGzy.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-DHyTqGG3.d.mts","names":[],"sources":["../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"mappings":";;;;;;;;;cASa;;;;;;;;;;;;;;;iBAoBG,mBAAmB,YAAY;;;;;iBAoB/B,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC6ElB,sBAAsB,cAAc,YACnD,QAAQ,eAAe,SACrB,wBAAwB;;;;iBAgCX,mBACf,QAAQ,iBACR,cACE;;;;;iBAgDa,yBACf,QAAQ,WACR,cACE;;;;;;;;;;;;cA8DU,6BAA6B;WACpB;EAArB,YAAqB;;;iBAUN,wBAAwB;iBAOxB,cACf,QAAQ,YAAY,iBACpB,cACE;;;;;;;iBA+Ba,wBACf,WAAW;;;;;;;;;;;;iBAkBI,kBACf,WAAW,qBACX;;;;;;;iBAsBe,kBAAkB,WAAW;;iBAa7B,kBAAkB,QAAQ;;;;;iBAS1B,gBACf,WAAW,qBACX,kBACE;;;;;iBAgCa,iBAAiB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoD5B,oBACf;EACK;EAAmB,YAAY;eAEpC"}
1
+ {"version":3,"file":"index-B-1lLGzy.d.mts","names":[],"sources":["../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"mappings":";;;;;;;;;cASa;;;;;;;;;;;;;;;iBAoBG,mBAAmB,YAAY;;;;;iBAoB/B,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC6ElB,sBAAsB,cAAc,YACnD,QAAQ,eAAe,SACrB,wBAAwB;;;;iBAgCX,mBACf,QAAQ,iBACR,cACE;;;;;iBAgDa,yBACf,QAAQ,WACR,cACE;;;;;;;;;;;;cA8DU,6BAA6B;WACpB;EAArB,YAAqB;;;iBAUN,wBAAwB;iBAOxB,cACf,QAAQ,YAAY,iBACpB,cACE;;;;;;;iBA+Ba,wBACf,WAAW;;;;;;;;;;;;iBAkBI,kBACf,WAAW,qBACX;;;;;;;iBAsBe,kBAAkB,WAAW;;iBAa7B,kBAAkB,QAAQ;;;;;iBAS1B,gBACf,WAAW,qBACX,kBACE;;;;;iBAgCa,iBAAiB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoD5B,oBACf;EACK;EAAmB,YAAY;eAEpC"}
@@ -1,4 +1,4 @@
1
- import { E as WorkspaceInput, S as NormalizedWorkspace, T as WorkspaceConfig, a as Routes, k as Framework, l as AppsRecord, r as GkmConfig, v as InferredWorkspaceConfig, y as LoadedConfig } from "./types-DwCQCkC-.cjs";
1
+ import { E as WorkspaceInput, S as NormalizedWorkspace, T as WorkspaceConfig, a as Routes, k as Framework, l as AppsRecord, r as GkmConfig, v as InferredWorkspaceConfig, y as LoadedConfig } from "./types-TzCChNrA.cjs";
2
2
  //#region src/workspace/publicEnv.d.ts
3
3
  /**
4
4
  * All public env-var prefixes the toolbox understands.
@@ -166,4 +166,4 @@ declare function getEndpointForStage(dokployConfig: {
166
166
  } | undefined, stage: string): string | undefined;
167
167
  //#endregion
168
168
  export { constructPatterns as a, getAppGkmConfig as c, processConfig as d, workspaceConstructGlobs as f, stripPublicPrefix as g, getPublicEnvPrefix as h, assertNoMovedDeployKeys as i, getEndpointForStage as l, PUBLIC_ENV_PREFIXES as m, allConstructGlobs as n, defineWorkspace as o, wrapSingleAppAsWorkspace as p, appConstructGlobs as r, getAppBuildOrder as s, DokployRegistryMoved as t, normalizeWorkspace as u };
169
- //# sourceMappingURL=index-Dg-UZ4Hm.d.cts.map
169
+ //# sourceMappingURL=index-vMidfqhU.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-Dg-UZ4Hm.d.cts","names":[],"sources":["../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"mappings":";;;;;;;;;cASa;;;;;;;;;;;;;;;iBAoBG,mBAAmB,YAAY;;;;;iBAoB/B,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC6ElB,sBAAsB,cAAc,YACnD,QAAQ,eAAe,SACrB,wBAAwB;;;;iBAgCX,mBACf,QAAQ,iBACR,cACE;;;;;iBAgDa,yBACf,QAAQ,WACR,cACE;;;;;;;;;;;;cA8DU,6BAA6B;WACpB;EAArB,YAAqB;;;iBAUN,wBAAwB;iBAOxB,cACf,QAAQ,YAAY,iBACpB,cACE;;;;;;;iBA+Ba,wBACf,WAAW;;;;;;;;;;;;iBAkBI,kBACf,WAAW,qBACX;;;;;;;iBAsBe,kBAAkB,WAAW;;iBAa7B,kBAAkB,QAAQ;;;;;iBAS1B,gBACf,WAAW,qBACX,kBACE;;;;;iBAgCa,iBAAiB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoD5B,oBACf;EACK;EAAmB,YAAY;eAEpC"}
1
+ {"version":3,"file":"index-vMidfqhU.d.cts","names":[],"sources":["../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"mappings":";;;;;;;;;cASa;;;;;;;;;;;;;;;iBAoBG,mBAAmB,YAAY;;;;;iBAoB/B,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC6ElB,sBAAsB,cAAc,YACnD,QAAQ,eAAe,SACrB,wBAAwB;;;;iBAgCX,mBACf,QAAQ,iBACR,cACE;;;;;iBAgDa,yBACf,QAAQ,WACR,cACE;;;;;;;;;;;;cA8DU,6BAA6B;WACpB;EAArB,YAAqB;;;iBAUN,wBAAwB;iBAOxB,cACf,QAAQ,YAAY,iBACpB,cACE;;;;;;;iBA+Ba,wBACf,WAAW;;;;;;;;;;;;iBAkBI,kBACf,WAAW,qBACX;;;;;;;iBAsBe,kBAAkB,WAAW;;iBAa7B,kBAAkB,QAAQ;;;;;iBAS1B,gBACf,WAAW,qBACX,kBACE;;;;;iBAgCa,iBAAiB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoD5B,oBACf;EACK;EAAmB,YAAY;eAEpC"}
package/dist/index.cjs CHANGED
@@ -7,19 +7,20 @@ const require_discover = require("./discover-D1It5r_h.cjs");
7
7
  const require_derive = require("./derive-DAbo49sm.cjs");
8
8
  const require_workspace = require("./workspace-vdrcRI-m.cjs");
9
9
  const require_config = require("./config.cjs");
10
- const require_state = require("./state-DMjFPOhf.cjs");
10
+ const require_state = require("./state-DCwRWyh-.cjs");
11
11
  const require_EndpointGenerator = require("./EndpointGenerator-CP6sBvQV.cjs");
12
12
  const require_sniffer = require("./sniffer-C-T7dSMN.cjs");
13
13
  const require_openapi = require("./openapi.cjs");
14
- const require_SubscriberGenerator = require("./SubscriberGenerator-Bixm28fN.cjs");
14
+ const require_SubscriberGenerator = require("./SubscriberGenerator-CajwOa5W.cjs");
15
15
  const require_appPorts = require("./appPorts-Cu79hJF6.cjs");
16
- const require_ready = require("./ready-BIuMw6wH.cjs");
16
+ const require_drivers = require("./drivers-BdvtCKc7.cjs");
17
17
  const require_emit = require("./emit-Dmc1nvdS.cjs");
18
- const require_deploy = require("./deploy-BitZLdnY.cjs");
19
- const require_compose = require("./compose-CNnw8meq.cjs");
18
+ const require_deploy = require("./deploy-eWorhvQp.cjs");
19
+ const require_compose = require("./compose-iuw8wfHk.cjs");
20
20
  const require_credentialSchemas = require("./credentialSchemas-DCR4Wd1W.cjs");
21
21
  const require_LocalStateStore = require("./LocalStateStore-BhrlQIXD.cjs");
22
22
  const require_utils = require("./utils-D2Nk1Nv2.cjs");
23
+ const require_ready = require("./ready-BB9xS4S9.cjs");
23
24
  let node_path = require("node:path");
24
25
  let commander = require("commander");
25
26
  let node_process = require("node:process");
@@ -52,7 +53,7 @@ crypto = require_rolldown_runtime.__toESM(crypto, 1);
52
53
  let _geekmidas_constructs_functions = require("@geekmidas/constructs/functions");
53
54
  let _geekmidas_constructs_topic = require("@geekmidas/constructs/topic");
54
55
  //#region package.json
55
- var version = "10.0.0-alpha.73";
56
+ var version = "10.0.0-alpha.74";
56
57
  //#endregion
57
58
  //#region src/prompt.ts
58
59
  /**
@@ -2041,9 +2042,9 @@ async function workspaceDevCommand(configured, options) {
2041
2042
  for (const { id, port } of fakes) logger$5.log(`🎭 ${id} fake: http://localhost:${port}`);
2042
2043
  logger$5.log(options.migrate || options.seed ? "🗄️ Migrating the databases" : "🗄️ Checking for pending migrations");
2043
2044
  if (options.migrate || options.seed) {
2044
- const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-kA_j1TPK.cjs"));
2045
+ const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-B0FCZNkp.cjs"));
2045
2046
  await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
2046
- } else await Promise.resolve().then(() => require("./migrate-kA_j1TPK.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
2047
+ } else await Promise.resolve().then(() => require("./migrate-B0FCZNkp.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
2047
2048
  logger$5.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
2048
2049
  });
2049
2050
  const appUrls = {};
@@ -2903,6 +2904,15 @@ var UnknownBuildProvider = class extends Error {
2903
2904
  this.name = "UnknownBuildProvider";
2904
2905
  }
2905
2906
  };
2907
+ /** `--cache` named something that is not a cache backend. */
2908
+ var UnknownCacheBackend = class extends Error {
2909
+ backend;
2910
+ constructor(backend) {
2911
+ super(`'${backend}' is not a cache backend. Pass --cache upstash, elasticache, db or redis, or leave it out to register the deploy target's.`);
2912
+ this.backend = backend;
2913
+ this.name = "UnknownCacheBackend";
2914
+ }
2915
+ };
2906
2916
  async function buildCommand(options) {
2907
2917
  const loadedConfig = await require_config.loadWorkspaceConfig();
2908
2918
  if (loadedConfig.type === "workspace") {
@@ -2955,7 +2965,7 @@ function backendsOf(workspace) {
2955
2965
  /** One app's build, from its config and the command's options. */
2956
2966
  async function buildOneApp(input) {
2957
2967
  const { config, options, workspace, appRoot, target } = input;
2958
- const cacheBackend = require_state.cacheBackendFor(require_state.providerOf(workspace));
2968
+ const cacheBackend = options.cache ?? require_state.cacheBackendFor(require_state.providerOf(workspace));
2959
2969
  const eventsBackend = require_state.eventsBackendFor(require_state.providerOf(workspace));
2960
2970
  const production = normalizeProductionConfig(options.production ?? false);
2961
2971
  if (production) logger$4.log(`🏭 Building for PRODUCTION`);
@@ -3042,7 +3052,7 @@ async function buildApp(input) {
3042
3052
  if (production?.bundle && !input.skipBundle) {
3043
3053
  const outputDir = (0, node_path.join)(appRoot, ".gkm", "server");
3044
3054
  const entryPoint = await writeSurfaceServer(outputDir, production);
3045
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-C35h0WKt.cjs"));
3055
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-utHuESvL.cjs"));
3046
3056
  await bundleServer({
3047
3057
  entryPoint,
3048
3058
  outputDir: (0, node_path.join)(outputDir, "dist"),
@@ -3062,7 +3072,7 @@ async function buildApp(input) {
3062
3072
  appRoot,
3063
3073
  databaseApi
3064
3074
  });
3065
- const eventsBackends = require_ready.eventsBackendsIn(declared, eventsBackend);
3075
+ const eventsBackends = require_drivers.eventsBackendsIn(declared, eventsBackend);
3066
3076
  const buildContext = {
3067
3077
  ...derived,
3068
3078
  telescope,
@@ -3071,9 +3081,9 @@ async function buildApp(input) {
3071
3081
  constructGlobs,
3072
3082
  cacheBackend,
3073
3083
  emailBackend: "ses",
3074
- storageDrivers: require_ready.driversFor({
3084
+ storageDrivers: require_drivers.driversFor({
3075
3085
  appRoot,
3076
- cache: require_ready.cacheBackendsIn(declared, cacheBackend),
3086
+ cache: require_drivers.cacheBackendsIn(declared, cacheBackend),
3077
3087
  events: eventsBackends
3078
3088
  }),
3079
3089
  eventsBackends,
@@ -3166,7 +3176,7 @@ async function buildWorkers(input) {
3166
3176
  });
3167
3177
  logger$4.log(`Generated worker ${worker.id}: ${crons.length} crons, ${queues.length} queues, ${subscribers.length} subscribers`);
3168
3178
  if (!input.bundle) continue;
3169
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-C35h0WKt.cjs"));
3179
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-utHuESvL.cjs"));
3170
3180
  const outfile = require_state.workerBundleName(worker.id);
3171
3181
  await bundleServer({
3172
3182
  entryPoint,
@@ -3236,7 +3246,7 @@ async function buildForTarget(target, context, appRoot, workspaceRoot, endpointG
3236
3246
  let masterKey;
3237
3247
  if (context.production?.bundle && !skipBundle) {
3238
3248
  logger$4.log(`\n📦 Bundling production server...`);
3239
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-C35h0WKt.cjs"));
3249
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-utHuESvL.cjs"));
3240
3250
  const allConstructs = [
3241
3251
  ...endpoints.map((e) => e.construct),
3242
3252
  ...functions.map((f) => f.construct),
@@ -4935,25 +4945,25 @@ function loadPackageJson() {
4935
4945
  * Run: pnpm --filter @geekmidas/cli sync-versions
4936
4946
  */
4937
4947
  const GEEKMIDAS_VERSIONS = {
4938
- "@geekmidas/audit": "~10.0.0-alpha.73",
4939
- "@geekmidas/auth": "~10.0.0-alpha.73",
4940
- "@geekmidas/cache": "~10.0.0-alpha.73",
4941
- "@geekmidas/client": "~10.0.0-alpha.73",
4942
- "@geekmidas/cloud": "~10.0.0-alpha.73",
4943
- "@geekmidas/constructs": "~10.0.0-alpha.73",
4944
- "@geekmidas/db": "~10.0.0-alpha.73",
4945
- "@geekmidas/emailkit": "~10.0.0-alpha.73",
4946
- "@geekmidas/envkit": "~10.0.0-alpha.73",
4947
- "@geekmidas/errors": "~10.0.0-alpha.73",
4948
- "@geekmidas/events": "~10.0.0-alpha.73",
4949
- "@geekmidas/logger": "~10.0.0-alpha.73",
4950
- "@geekmidas/manifest": "~10.0.0-alpha.73",
4951
- "@geekmidas/rate-limit": "~10.0.0-alpha.73",
4952
- "@geekmidas/schema": "~10.0.0-alpha.73",
4953
- "@geekmidas/services": "~10.0.0-alpha.73",
4954
- "@geekmidas/storage": "~10.0.0-alpha.73",
4955
- "@geekmidas/telescope": "~10.0.0-alpha.73",
4956
- "@geekmidas/testkit": "~10.0.0-alpha.73",
4948
+ "@geekmidas/audit": "~10.0.0-alpha.74",
4949
+ "@geekmidas/auth": "~10.0.0-alpha.74",
4950
+ "@geekmidas/cache": "~10.0.0-alpha.74",
4951
+ "@geekmidas/client": "~10.0.0-alpha.74",
4952
+ "@geekmidas/cloud": "~10.0.0-alpha.74",
4953
+ "@geekmidas/constructs": "~10.0.0-alpha.74",
4954
+ "@geekmidas/db": "~10.0.0-alpha.74",
4955
+ "@geekmidas/emailkit": "~10.0.0-alpha.74",
4956
+ "@geekmidas/envkit": "~10.0.0-alpha.74",
4957
+ "@geekmidas/errors": "~10.0.0-alpha.74",
4958
+ "@geekmidas/events": "~10.0.0-alpha.74",
4959
+ "@geekmidas/logger": "~10.0.0-alpha.74",
4960
+ "@geekmidas/manifest": "~10.0.0-alpha.74",
4961
+ "@geekmidas/rate-limit": "~10.0.0-alpha.74",
4962
+ "@geekmidas/schema": "~10.0.0-alpha.74",
4963
+ "@geekmidas/services": "~10.0.0-alpha.74",
4964
+ "@geekmidas/storage": "~10.0.0-alpha.74",
4965
+ "@geekmidas/telescope": "~10.0.0-alpha.74",
4966
+ "@geekmidas/testkit": "~10.0.0-alpha.74",
4957
4967
  "@geekmidas/cli": `~${loadPackageJson().version}`
4958
4968
  };
4959
4969
  //#endregion
@@ -10744,7 +10754,7 @@ function testDatabases(reconciled) {
10744
10754
  * starts without the reference data the app needs.
10745
10755
  */
10746
10756
  async function migrateTestStage(workspace, env) {
10747
- const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-kA_j1TPK.cjs"));
10757
+ const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-B0FCZNkp.cjs"));
10748
10758
  const { discover } = await Promise.resolve().then(() => require("./discover-D1It5r_h.cjs")).then((n) => n.discover_exports);
10749
10759
  const sources = {};
10750
10760
  const manifest = await discover({
@@ -11110,13 +11120,15 @@ program.command("setup").description("Reconcile declared constructs — containe
11110
11120
  process.exit(1);
11111
11121
  }
11112
11122
  });
11113
- program.command("build").description("Build handlers and the manifest from declared constructs").option("--provider <provider>", "Build for aws or server (default: where gkm.config.ts deploys)").option("--enable-openapi", "Enable OpenAPI documentation generation for server builds").option("--production", "Build for production (no dev tools, bundled output)").option("--skip-bundle", "Skip bundling step in production build").option("--stage <stage>", "Inject encrypted secrets for deployment stage").option("--mark-optional", "Suffix optional env vars with ? in manifest envVars field (e.g. PORT?)").action(async (options) => {
11123
+ program.command("build").description("Build handlers and the manifest from declared constructs").option("--provider <provider>", "Build for aws or server (default: where gkm.config.ts deploys)").option("--enable-openapi", "Enable OpenAPI documentation generation for server builds").option("--production", "Build for production (no dev tools, bundled output)").option("--skip-bundle", "Skip bundling step in production build").option("--stage <stage>", "Inject encrypted secrets for deployment stage").option("--mark-optional", "Suffix optional env vars with ? in manifest envVars field (e.g. PORT?)").addOption(new commander.Option("--cache <backend>", "Register the drivers for this cache backend rather than the target default").hideHelp()).action(async (options) => {
11114
11124
  try {
11115
11125
  const globalOptions = program.opts();
11116
11126
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11117
11127
  if (options.provider !== void 0 && !isMainProvider(options.provider)) throw new UnknownBuildProvider(options.provider);
11128
+ if (options.cache !== void 0 && !require_SubscriberGenerator.isCacheBackend(options.cache)) throw new UnknownCacheBackend(options.cache);
11118
11129
  await buildCommand({
11119
11130
  ...options.provider ? { provider: options.provider } : {},
11131
+ ...options.cache ? { cache: options.cache } : {},
11120
11132
  enableOpenApi: options.enableOpenapi || false,
11121
11133
  production: options.production || false,
11122
11134
  skipBundle: options.skipBundle || false,
@@ -11175,7 +11187,7 @@ program.command("migrate").description("Apply each database construct's migratio
11175
11187
  try {
11176
11188
  const globalOptions = program.opts();
11177
11189
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11178
- const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-kA_j1TPK.cjs"));
11190
+ const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-B0FCZNkp.cjs"));
11179
11191
  await migrateCommand({
11180
11192
  ...options,
11181
11193
  ...construct ? { construct } : {}
@@ -11189,7 +11201,7 @@ program.command("seed").description("Migrate, then run each database construct's
11189
11201
  try {
11190
11202
  const globalOptions = program.opts();
11191
11203
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11192
- const { seedCommand } = await Promise.resolve().then(() => require("./migrate-kA_j1TPK.cjs"));
11204
+ const { seedCommand } = await Promise.resolve().then(() => require("./migrate-B0FCZNkp.cjs"));
11193
11205
  await seedCommand({
11194
11206
  ...options,
11195
11207
  ...construct ? { construct } : {}
@@ -11203,7 +11215,7 @@ program.command("migration").description("Write a construct's next migration: an
11203
11215
  try {
11204
11216
  const globalOptions = program.opts();
11205
11217
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11206
- const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-kA_j1TPK.cjs"));
11218
+ const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-B0FCZNkp.cjs"));
11207
11219
  await migrationCommand({
11208
11220
  construct,
11209
11221
  ...name ? { name } : {}
@@ -11297,7 +11309,7 @@ program.command("secrets:add").description("Build the keys a stage must be given
11297
11309
  try {
11298
11310
  const globalOptions = program.opts();
11299
11311
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11300
- const { secretsAddCommand } = await Promise.resolve().then(() => require("./add-C1ojB2Yy.cjs"));
11312
+ const { secretsAddCommand } = await Promise.resolve().then(() => require("./add-Djrr12Xc.cjs"));
11301
11313
  await secretsAddCommand(options);
11302
11314
  } catch (error) {
11303
11315
  console.error(formatError(error));
@@ -11308,7 +11320,7 @@ program.command("encryption:rotate").description("Add a new current key to an En
11308
11320
  try {
11309
11321
  const globalOptions = program.opts();
11310
11322
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11311
- const { encryptionRotateCommand } = await Promise.resolve().then(() => require("./encryption-CDGRdcMr.cjs"));
11323
+ const { encryptionRotateCommand } = await Promise.resolve().then(() => require("./encryption-DSkJdoyk.cjs"));
11312
11324
  await encryptionRotateCommand(construct, options);
11313
11325
  } catch (error) {
11314
11326
  console.error(formatError(error));
@@ -11319,7 +11331,7 @@ program.command("encryption:retire").description("Remove an old key from an Encr
11319
11331
  try {
11320
11332
  const globalOptions = program.opts();
11321
11333
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11322
- const { encryptionRetireCommand } = await Promise.resolve().then(() => require("./encryption-CDGRdcMr.cjs"));
11334
+ const { encryptionRetireCommand } = await Promise.resolve().then(() => require("./encryption-DSkJdoyk.cjs"));
11323
11335
  await encryptionRetireCommand(construct, key, options);
11324
11336
  } catch (error) {
11325
11337
  console.error(formatError(error));
@@ -11362,7 +11374,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
11362
11374
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11363
11375
  const { loadWorkspaceConfig } = await Promise.resolve().then(() => require("./config.cjs"));
11364
11376
  const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CuCQBa5-.cjs"));
11365
- const { secretsStoreFor } = await Promise.resolve().then(() => require("./state-DMjFPOhf.cjs")).then((n) => n.store_exports);
11377
+ const { secretsStoreFor } = await Promise.resolve().then(() => require("./state-DCwRWyh-.cjs")).then((n) => n.store_exports);
11366
11378
  const { workspace } = await loadWorkspaceConfig();
11367
11379
  const stage = options.stage ?? workspace.stages.local;
11368
11380
  const store = await secretsStoreFor(workspace, stage);
@@ -11371,7 +11383,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
11371
11383
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
11372
11384
  process.exit(1);
11373
11385
  }
11374
- const { derivedContainers } = await Promise.resolve().then(() => require("./state-DMjFPOhf.cjs")).then((n) => n.workspace_exports);
11386
+ const { derivedContainers } = await Promise.resolve().then(() => require("./state-DCwRWyh-.cjs")).then((n) => n.workspace_exports);
11375
11387
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
11376
11388
  if (!result) {
11377
11389
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
@@ -11386,7 +11398,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
11386
11398
  }
11387
11399
  });
11388
11400
  program.command("deploy").description("Deploy a stage through its target").option("--target <name>", "Deploy target: dokploy, compose, sst, or one named in deploy.targets (default: deploy.default)").option("--provider <provider>", "[DEPRECATED] Use --target instead").requiredOption("--stage <stage>", "Deployment stage (e.g., production, staging)").option("--tag <tag>", "Image tag (default: stage-timestamp; compose: the commit). Compose pulls a given tag").option("--skip-push", "Skip pushing image to registry").option("--skip-build", "Skip build step (use existing build)").option("--json", "Write events as JSON lines instead of progress; never prompts").option("--dry-run", "Show what would be created or reused; change, build and push nothing").option("--atomic", "If the release fails, roll back every app it released, not only the failed ones").option("--allow-dev-services <list>", "Server targets: run MinIO and/or Mailpit (minio,mailpit) for mail and buckets the stage's secrets don't configure. Not production-grade").action(async (options) => {
11389
- const { deployCli } = await Promise.resolve().then(() => require("./cli-DJVL2sL9.cjs"));
11401
+ const { deployCli } = await Promise.resolve().then(() => require("./cli-Bx0sUzxH.cjs"));
11390
11402
  const globalOptions = program.opts();
11391
11403
  const code = await deployCli({
11392
11404
  cwd: (0, node_path.resolve)(globalOptions.cwd ?? process.cwd()),
@@ -11403,7 +11415,7 @@ program.command("deploy").description("Deploy a stage through its target").optio
11403
11415
  });
11404
11416
  program.command("deploy:rollback").description("Put a stage's app back on its previous release (Dokploy): one app with --app, every app with --atomic").requiredOption("--stage <stage>", "A deployed stage").option("--app <app>", "The app to roll back").option("--atomic", "Roll back every app that has an earlier release").action(async (options) => {
11405
11417
  try {
11406
- const { rollbackStage } = await Promise.resolve().then(() => require("./rollback-QGwomD64.cjs")).then((n) => n.rollback_exports);
11418
+ const { rollbackStage } = await Promise.resolve().then(() => require("./rollback-DJy3t696.cjs")).then((n) => n.rollback_exports);
11407
11419
  const { terminalCredentials } = await Promise.resolve().then(() => require("./terminal-BL5E0xbR.cjs"));
11408
11420
  const globalOptions = program.opts();
11409
11421
  await rollbackStage({
@@ -11439,7 +11451,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
11439
11451
  try {
11440
11452
  const globalOptions = program.opts();
11441
11453
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11442
- const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-C6qF8kHg.cjs"));
11454
+ const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-BvN3CfxM.cjs"));
11443
11455
  await deployGithubCommand(options);
11444
11456
  } catch (error) {
11445
11457
  console.error(formatError(error));