@geekmidas/cli 10.0.0-alpha.97 → 10.0.0-alpha.99

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 (209) hide show
  1. package/dist/{GoDaddyProvider-AqNu60WQ.mjs → GoDaddyProvider-206NguW1.mjs} +2 -2
  2. package/dist/{GoDaddyProvider-AqNu60WQ.mjs.map → GoDaddyProvider-206NguW1.mjs.map} +1 -1
  3. package/dist/{GoDaddyProvider-B4R_QFl8.cjs → GoDaddyProvider-D1aQy2i1.cjs} +2 -2
  4. package/dist/{GoDaddyProvider-B4R_QFl8.cjs.map → GoDaddyProvider-D1aQy2i1.cjs.map} +1 -1
  5. package/dist/{LegacyStateStore-B-aRqRdD.cjs → LegacyStateStore-CgJEr1G7.cjs} +3 -3
  6. package/dist/{LegacyStateStore-B-aRqRdD.cjs.map → LegacyStateStore-CgJEr1G7.cjs.map} +1 -1
  7. package/dist/{LegacyStateStore-8F05-BOo.mjs → LegacyStateStore-wmFfdzuh.mjs} +3 -3
  8. package/dist/{LegacyStateStore-8F05-BOo.mjs.map → LegacyStateStore-wmFfdzuh.mjs.map} +1 -1
  9. package/dist/{LocalStateStore-B_bRI8M7.cjs → LocalStateStore-BD14kra9.cjs} +2 -2
  10. package/dist/{LocalStateStore-B_bRI8M7.cjs.map → LocalStateStore-BD14kra9.cjs.map} +1 -1
  11. package/dist/{LocalStateStore-B_mfgcO8.mjs → LocalStateStore-C5mNp91D.mjs} +2 -2
  12. package/dist/{LocalStateStore-B_mfgcO8.mjs.map → LocalStateStore-C5mNp91D.mjs.map} +1 -1
  13. package/dist/{S3StateStore-CMN0Uzdp.mjs → S3StateStore-CEGbsDHi.mjs} +10 -9
  14. package/dist/S3StateStore-CEGbsDHi.mjs.map +1 -0
  15. package/dist/{S3StateStore-zK9zcd9f.cjs → S3StateStore-Csu_kJCy.cjs} +11 -10
  16. package/dist/S3StateStore-Csu_kJCy.cjs.map +1 -0
  17. package/dist/{SSMStateStore-Cv7VcMak.mjs → SSMStateStore-Bd62qwp4.mjs} +2 -2
  18. package/dist/{SSMStateStore-Cv7VcMak.mjs.map → SSMStateStore-Bd62qwp4.mjs.map} +1 -1
  19. package/dist/{SSMStateStore-C489DnR6.cjs → SSMStateStore-Rp21jtP5.cjs} +2 -2
  20. package/dist/{SSMStateStore-C489DnR6.cjs.map → SSMStateStore-Rp21jtP5.cjs.map} +1 -1
  21. package/dist/{add-BA5d4pA0.mjs → add-DyIEYI97.mjs} +4 -4
  22. package/dist/{add-BA5d4pA0.mjs.map → add-DyIEYI97.mjs.map} +1 -1
  23. package/dist/{add-HXzTCy_I.cjs → add-Iuo3ao5E.cjs} +5 -5
  24. package/dist/{add-HXzTCy_I.cjs.map → add-Iuo3ao5E.cjs.map} +1 -1
  25. package/dist/{aws-Dz2bYxK1.mjs → aws-8QQ8n-t6.mjs} +2 -2
  26. package/dist/{aws-Dz2bYxK1.mjs.map → aws-8QQ8n-t6.mjs.map} +1 -1
  27. package/dist/{aws-BTReGjpY.cjs → aws-DY9NqQIT.cjs} +2 -2
  28. package/dist/{aws-BTReGjpY.cjs.map → aws-DY9NqQIT.cjs.map} +1 -1
  29. package/dist/{awsStore-BWa1KNY-.mjs → awsStore-Bx5nCFnS.mjs} +8 -6
  30. package/dist/awsStore-Bx5nCFnS.mjs.map +1 -0
  31. package/dist/{awsStore-B5We7bMg.cjs → awsStore-u3ehhMoY.cjs} +8 -6
  32. package/dist/awsStore-u3ehhMoY.cjs.map +1 -0
  33. package/dist/{bundler-SX3Uy8qT.cjs → bundler-Bgoq8IyR.cjs} +5 -5
  34. package/dist/{bundler-SX3Uy8qT.cjs.map → bundler-Bgoq8IyR.cjs.map} +1 -1
  35. package/dist/{bundler-CBomFsnd.mjs → bundler-Q6Kp8b0r.mjs} +5 -5
  36. package/dist/{bundler-CBomFsnd.mjs.map → bundler-Q6Kp8b0r.mjs.map} +1 -1
  37. package/dist/{cli-D7z1lQtO.mjs → cli-B9BudUV0.mjs} +2 -4
  38. package/dist/cli-B9BudUV0.mjs.map +1 -0
  39. package/dist/{cli-1l0L9LCA.cjs → cli-W2DMRIbP.cjs} +2 -4
  40. package/dist/cli-W2DMRIbP.cjs.map +1 -0
  41. package/dist/compose-CIrFNWHb.cjs +3147 -0
  42. package/dist/compose-CIrFNWHb.cjs.map +1 -0
  43. package/dist/compose-CLsRcHkA.mjs +3052 -0
  44. package/dist/compose-CLsRcHkA.mjs.map +1 -0
  45. package/dist/{config-xIWIaDhW.d.cts → config-BVdHfj8b.d.cts} +4 -4
  46. package/dist/{config-D-k0gaAr.d.mts.map → config-BVdHfj8b.d.cts.map} +1 -1
  47. package/dist/{config-D-k0gaAr.d.mts → config-D2OqCmAM.d.mts} +4 -4
  48. package/dist/{config-xIWIaDhW.d.cts.map → config-D2OqCmAM.d.mts.map} +1 -1
  49. package/dist/config.cjs +1 -1
  50. package/dist/config.d.cts +3 -3
  51. package/dist/config.d.mts +3 -3
  52. package/dist/config.mjs +1 -1
  53. package/dist/deploy/public.cjs +9 -8
  54. package/dist/deploy/public.cjs.map +1 -1
  55. package/dist/deploy/public.d.cts +5 -16
  56. package/dist/deploy/public.d.cts.map +1 -1
  57. package/dist/deploy/public.d.mts +5 -16
  58. package/dist/deploy/public.d.mts.map +1 -1
  59. package/dist/deploy/public.mjs +8 -7
  60. package/dist/deploy/public.mjs.map +1 -1
  61. package/dist/{deploy-Cu_HyWoJ.cjs → deploy-CbJMJsgX.cjs} +26 -62
  62. package/dist/deploy-CbJMJsgX.cjs.map +1 -0
  63. package/dist/{deploy-LY-20Yeb.mjs → deploy-n_IB8r6H.mjs} +17 -59
  64. package/dist/deploy-n_IB8r6H.mjs.map +1 -0
  65. package/dist/{discover-CbJCQq4J.d.mts → discover-Caf6Hnv1.d.cts} +2 -2
  66. package/dist/{discover-CbJCQq4J.d.mts.map → discover-Caf6Hnv1.d.cts.map} +1 -1
  67. package/dist/{discover-RCW5mBXb.d.cts → discover-Cd5M2jPv.d.mts} +2 -2
  68. package/dist/{discover-RCW5mBXb.d.cts.map → discover-Cd5M2jPv.d.mts.map} +1 -1
  69. package/dist/{dns-BbTOzmVp.mjs → dns-C_LPS3qV.mjs} +3 -3
  70. package/dist/{dns-BbTOzmVp.mjs.map → dns-C_LPS3qV.mjs.map} +1 -1
  71. package/dist/{dns-BFnoawgh.cjs → dns-kp8To8Qj.cjs} +3 -3
  72. package/dist/{dns-BFnoawgh.cjs.map → dns-kp8To8Qj.cjs.map} +1 -1
  73. package/dist/{dokploy-LR94sBVI.mjs → dokploy-Dl_kNVeE.mjs} +2 -2
  74. package/dist/{dokploy-LR94sBVI.mjs.map → dokploy-Dl_kNVeE.mjs.map} +1 -1
  75. package/dist/{dokploy-CKgSww82.cjs → dokploy-gRIae9Ht.cjs} +2 -2
  76. package/dist/{dokploy-CKgSww82.cjs.map → dokploy-gRIae9Ht.cjs.map} +1 -1
  77. package/dist/{encryption-C0plOf4a.mjs → encryption-C3DcVZQs.mjs} +3 -3
  78. package/dist/{encryption-C0plOf4a.mjs.map → encryption-C3DcVZQs.mjs.map} +1 -1
  79. package/dist/{encryption-CUyw-8sc.cjs → encryption-CmQvQOSe.cjs} +3 -3
  80. package/dist/{encryption-CUyw-8sc.cjs.map → encryption-CmQvQOSe.cjs.map} +1 -1
  81. package/dist/{engine-DqbqQhck.mjs → engine-DBXuhHyf.mjs} +5 -4
  82. package/dist/{engine-DqbqQhck.mjs.map → engine-DBXuhHyf.mjs.map} +1 -1
  83. package/dist/{engine-D8TeraDK.cjs → engine-Dk19Mxc4.cjs} +27 -26
  84. package/dist/{engine-D8TeraDK.cjs.map → engine-Dk19Mxc4.cjs.map} +1 -1
  85. package/dist/{github-DC4SIqV1.cjs → github-Ds2RYnSU.cjs} +43 -20
  86. package/dist/github-Ds2RYnSU.cjs.map +1 -0
  87. package/dist/{github-zgaBOQvh.mjs → github-RawRLERq.mjs} +42 -19
  88. package/dist/github-RawRLERq.mjs.map +1 -0
  89. package/dist/identity-CBXr4wNi.mjs +129 -0
  90. package/dist/identity-CBXr4wNi.mjs.map +1 -0
  91. package/dist/identity-CbLfgQNT.cjs +182 -0
  92. package/dist/identity-CbLfgQNT.cjs.map +1 -0
  93. package/dist/{index-ClqOsbBl.d.mts → index-BU4G20G6.d.cts} +2 -2
  94. package/dist/{index-ClqOsbBl.d.mts.map → index-BU4G20G6.d.cts.map} +1 -1
  95. package/dist/{index-DgOBAEl5.d.cts → index-yDbd1WcM.d.mts} +2 -2
  96. package/dist/{index-DgOBAEl5.d.cts.map → index-yDbd1WcM.d.mts.map} +1 -1
  97. package/dist/index.cjs +156 -151
  98. package/dist/index.cjs.map +1 -1
  99. package/dist/index.mjs +153 -148
  100. package/dist/index.mjs.map +1 -1
  101. package/dist/{migrate-BJ8aLOWk.mjs → migrate-CZIGT3kB.mjs} +3 -3
  102. package/dist/{migrate-BJ8aLOWk.mjs.map → migrate-CZIGT3kB.mjs.map} +1 -1
  103. package/dist/migrate-CxZYzF_u.cjs +181 -0
  104. package/dist/migrate-CxZYzF_u.cjs.map +1 -0
  105. package/dist/migrate-DR1x96I4.mjs +181 -0
  106. package/dist/migrate-DR1x96I4.mjs.map +1 -0
  107. package/dist/{migrate-CWq8-s21.cjs → migrate-DjrTwWJA.cjs} +3 -3
  108. package/dist/{migrate-CWq8-s21.cjs.map → migrate-DjrTwWJA.cjs.map} +1 -1
  109. package/dist/openapi.cjs +1 -1
  110. package/dist/openapi.d.cts +1 -1
  111. package/dist/openapi.d.mts +1 -1
  112. package/dist/openapi.mjs +1 -1
  113. package/dist/{projectBucket-BFp-SFee.mjs → projectBucket-BYOxRnHY.mjs} +2 -2
  114. package/dist/{projectBucket-BFp-SFee.mjs.map → projectBucket-BYOxRnHY.mjs.map} +1 -1
  115. package/dist/{projectBucket-Cho9DpSh.cjs → projectBucket-CDu5rXjp.cjs} +2 -2
  116. package/dist/{projectBucket-Cho9DpSh.cjs.map → projectBucket-CDu5rXjp.cjs.map} +1 -1
  117. package/dist/reconcile/public.d.cts +2 -2
  118. package/dist/reconcile/public.d.mts +2 -2
  119. package/dist/{resolve-6EYNqYdr.mjs → resolve-CNYbGH8g.mjs} +3 -3
  120. package/dist/{resolve-6EYNqYdr.mjs.map → resolve-CNYbGH8g.mjs.map} +1 -1
  121. package/dist/{resolve-C8VkR4oW.cjs → resolve-DC98Rl1t.cjs} +3 -3
  122. package/dist/{resolve-C8VkR4oW.cjs.map → resolve-DC98Rl1t.cjs.map} +1 -1
  123. package/dist/{rollback-BsThTeSN.mjs → rollback-BkIkE8Qe.mjs} +5 -5
  124. package/dist/{rollback-BsThTeSN.mjs.map → rollback-BkIkE8Qe.mjs.map} +1 -1
  125. package/dist/{rollback-CvGfm4DX.cjs → rollback-DBfLvC8k.cjs} +5 -5
  126. package/dist/{rollback-CvGfm4DX.cjs.map → rollback-DBfLvC8k.cjs.map} +1 -1
  127. package/dist/{runtime-CaW4QqF2.d.cts → runtime-CLz3bf07.d.mts} +2 -2
  128. package/dist/{runtime-CaW4QqF2.d.cts.map → runtime-CLz3bf07.d.mts.map} +1 -1
  129. package/dist/{runtime-K5uxp6HE.d.mts → runtime-CUWf0IUk.d.cts} +2 -2
  130. package/dist/{runtime-K5uxp6HE.d.mts.map → runtime-CUWf0IUk.d.cts.map} +1 -1
  131. package/dist/s3-BwhQk3QM.mjs +215 -0
  132. package/dist/s3-BwhQk3QM.mjs.map +1 -0
  133. package/dist/s3-DLp0V-Ox.cjs +215 -0
  134. package/dist/s3-DLp0V-Ox.cjs.map +1 -0
  135. package/dist/{sandbox-D8cfZUAA.d.cts → sandbox-CfpLWTPP.d.mts} +2 -2
  136. package/dist/{sandbox-B_BkfmTP.d.mts.map → sandbox-CfpLWTPP.d.mts.map} +1 -1
  137. package/dist/{sandbox-B_BkfmTP.d.mts → sandbox-D1XeNEJ9.d.cts} +2 -2
  138. package/dist/{sandbox-D8cfZUAA.d.cts.map → sandbox-D1XeNEJ9.d.cts.map} +1 -1
  139. package/dist/{secretsManager-C6wm-9uZ.mjs → secretsManager-CQORubp9.mjs} +2 -2
  140. package/dist/{secretsManager-C6wm-9uZ.mjs.map → secretsManager-CQORubp9.mjs.map} +1 -1
  141. package/dist/{secretsManager-Cwwgs81g.cjs → secretsManager-RAGcEDX1.cjs} +2 -2
  142. package/dist/{secretsManager-Cwwgs81g.cjs.map → secretsManager-RAGcEDX1.cjs.map} +1 -1
  143. package/dist/{seeds-BQlj9Fsg.cjs → seeds-BNHSpjNP.cjs} +22 -166
  144. package/dist/seeds-BNHSpjNP.cjs.map +1 -0
  145. package/dist/{seeds-BsJRUL1K.mjs → seeds-C4bZ8L-T.mjs} +23 -119
  146. package/dist/seeds-C4bZ8L-T.mjs.map +1 -0
  147. package/dist/{sniffer-DVsvP8s-.mjs → sniffer-BIBm9dAb.mjs} +7 -7
  148. package/dist/{sniffer-DVsvP8s-.mjs.map → sniffer-BIBm9dAb.mjs.map} +1 -1
  149. package/dist/{sniffer-CeUKUoHn.cjs → sniffer-Cylp9W2D.cjs} +7 -7
  150. package/dist/{sniffer-CeUKUoHn.cjs.map → sniffer-Cylp9W2D.cjs.map} +1 -1
  151. package/dist/{stages-DEmn7qI8.mjs → stages-CcS9vnNu.mjs} +5 -18
  152. package/dist/stages-CcS9vnNu.mjs.map +1 -0
  153. package/dist/{stages-BpDXFPGg.cjs → stages-DdBV7msu.cjs} +5 -18
  154. package/dist/stages-DdBV7msu.cjs.map +1 -0
  155. package/dist/target/public.cjs +1 -1
  156. package/dist/target/public.d.cts +2 -2
  157. package/dist/target/public.d.mts +2 -2
  158. package/dist/target/public.mjs +1 -1
  159. package/dist/{types-4vPS_YFX.d.cts → types-BhThob0H.d.mts} +90 -21
  160. package/dist/types-BhThob0H.d.mts.map +1 -0
  161. package/dist/{types-CFsEyFb1.d.mts → types-DwoLHelT.d.cts} +91 -20
  162. package/dist/types-DwoLHelT.d.cts.map +1 -0
  163. package/dist/workspace/index.cjs +1 -1
  164. package/dist/workspace/index.d.cts +2 -2
  165. package/dist/workspace/index.d.mts +2 -2
  166. package/dist/workspace/index.mjs +1 -1
  167. package/dist/{workspace-Bv4xROeu.mjs → workspace-D5CpOp1y.mjs} +77 -5
  168. package/dist/workspace-D5CpOp1y.mjs.map +1 -0
  169. package/dist/{workspace-C1M-S4al.cjs → workspace-DRjdu1OY.cjs} +88 -4
  170. package/dist/workspace-DRjdu1OY.cjs.map +1 -0
  171. package/package.json +15 -15
  172. package/dist/S3StateStore-CMN0Uzdp.mjs.map +0 -1
  173. package/dist/S3StateStore-zK9zcd9f.cjs.map +0 -1
  174. package/dist/awsStore-B5We7bMg.cjs.map +0 -1
  175. package/dist/awsStore-BWa1KNY-.mjs.map +0 -1
  176. package/dist/cli-1l0L9LCA.cjs.map +0 -1
  177. package/dist/cli-D7z1lQtO.mjs.map +0 -1
  178. package/dist/compose-C39XupJu.cjs +0 -1558
  179. package/dist/compose-C39XupJu.cjs.map +0 -1
  180. package/dist/compose-DgWrNd2k.mjs +0 -1511
  181. package/dist/compose-DgWrNd2k.mjs.map +0 -1
  182. package/dist/deploy-Cu_HyWoJ.cjs.map +0 -1
  183. package/dist/deploy-LY-20Yeb.mjs.map +0 -1
  184. package/dist/dns-Bmbhmz41.mjs +0 -44
  185. package/dist/dns-Bmbhmz41.mjs.map +0 -1
  186. package/dist/dns-Dro_vbk9.cjs +0 -55
  187. package/dist/dns-Dro_vbk9.cjs.map +0 -1
  188. package/dist/github-DC4SIqV1.cjs.map +0 -1
  189. package/dist/github-zgaBOQvh.mjs.map +0 -1
  190. package/dist/migrate-C230QeXU.mjs +0 -94
  191. package/dist/migrate-C230QeXU.mjs.map +0 -1
  192. package/dist/migrate-CsMrI7Dk.cjs +0 -94
  193. package/dist/migrate-CsMrI7Dk.cjs.map +0 -1
  194. package/dist/secrets-BTY6JhTp.cjs +0 -47
  195. package/dist/secrets-BTY6JhTp.cjs.map +0 -1
  196. package/dist/secrets-HzKNTWg9.mjs +0 -36
  197. package/dist/secrets-HzKNTWg9.mjs.map +0 -1
  198. package/dist/seeds-BQlj9Fsg.cjs.map +0 -1
  199. package/dist/seeds-BsJRUL1K.mjs.map +0 -1
  200. package/dist/stack-BQOyqaVG.mjs +0 -1201
  201. package/dist/stack-BQOyqaVG.mjs.map +0 -1
  202. package/dist/stack-Clzi40EE.cjs +0 -1308
  203. package/dist/stack-Clzi40EE.cjs.map +0 -1
  204. package/dist/stages-BpDXFPGg.cjs.map +0 -1
  205. package/dist/stages-DEmn7qI8.mjs.map +0 -1
  206. package/dist/types-4vPS_YFX.d.cts.map +0 -1
  207. package/dist/types-CFsEyFb1.d.mts.map +0 -1
  208. package/dist/workspace-Bv4xROeu.mjs.map +0 -1
  209. package/dist/workspace-C1M-S4al.cjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"github-DC4SIqV1.cjs","names":["GkmError","secretsParameterName","secretsManagerSecretName","projectBucketName","deployIdentity","PROJECT_BUCKET_CREATE_ACTIONS","stageProvider","bucketName","iamUserName","IAM_USER_PATH","stageDnsDomains","execFileSync","ListOpenIDConnectProvidersCommand","CreateOpenIDConnectProviderCommand","GetRoleCommand","UpdateAssumeRolePolicyCommand","ListRoleTagsCommand","CreateRoleCommand","DetachRolePolicyCommand","DeleteRolePolicyCommand","AttachRolePolicyCommand","TagRoleCommand","PutRolePolicyCommand","UntagRoleCommand","ListAttachedRolePoliciesCommand","IAMClient","loadWorkspaceConfig","discover","constructGlobs","readKey","keystoreProject","getKeyPath","isRemoteStore","secretsStoreFor","GkmError"],"sources":["../src/deploy/githubOidc.ts","../src/deploy/githubPolicy.ts","../src/deploy/github.ts"],"sourcesContent":["/**\n * The `sub` GitHub puts in a deploy job's OIDC token — what the deploy role's\n * trust policy has to match exactly.\n *\n * It is not always `repo:<owner>/<name>:environment:<stage>`. A repository\n * (or its organisation) decides the format, and says which through\n * `GET /repos/{owner}/{repo}/actions/oidc/customization/sub`:\n *\n * - the default: `repo:<owner>/<name>:environment:<stage>`;\n * - the immutable subject — the default for repositories created after\n * 15 July 2026, opt-in before — puts the owner's and the repository's ids in\n * the `repo` segment: `repo:<owner>@<ownerId>/<name>@<repoId>:environment:<stage>`.\n * The API hands that segment back as `sub_claim_prefix`;\n * - a custom template (`use_default: false`) builds the `sub` from\n * `include_claim_keys`, in order, each as `<key>:<value>` joined by `:`,\n * where `context` is `environment:<stage>` in an environment job.\n *\n * A `:` inside a value is sent as `%3A`.\n *\n * See https://docs.github.com/en/actions/reference/security/oidc.\n */\n\nimport { GkmError } from '../errors';\nimport type { Gh } from './github.js';\n\n/** `GET /repos/{owner}/{repo}/actions/oidc/customization/sub`. */\nexport interface OidcSubjectCustomization {\n\tuse_default: boolean;\n\tinclude_claim_keys?: string[];\n\tuse_immutable_subject?: boolean;\n\t/** The `repo` segment of the subject, as GitHub will send it. */\n\tsub_claim_prefix?: string;\n}\n\n/** What `GET /repos/{owner}/{repo}` says that a subject may include. */\nexport interface RepoFacts {\n\townerId: number;\n\trepoId: number;\n\tvisibility: string;\n}\n\n/**\n * Template keys whose values are known for a repository's environment job\n * before the job runs. Anything else — `job_workflow_ref`, `ref`, `sha`,\n * `run_id`, `workflow`, `actor`, a custom property — depends on the run.\n */\nexport const SUPPORTED_CLAIM_KEYS = [\n\t'repo',\n\t'context',\n\t'environment',\n\t'repository',\n\t'repository_owner',\n\t'repository_owner_id',\n\t'repository_id',\n\t'repository_visibility',\n] as const;\n\nconst NEEDS_FACTS = new Set([\n\t'repository_owner_id',\n\t'repository_id',\n\t'repository_visibility',\n]);\n\n/** A `:` inside a claim value is sent as `%3A`. */\nfunction claim(value: string): string {\n\treturn value.replaceAll(':', '%3A');\n}\n\n/** Which kind of subject the repository uses, for the output. */\nexport type OidcSubjectKind = 'default' | 'immutable' | 'custom' | 'assumed';\n\n/** Whether {@link oidcSubject} needs the repository's ids or visibility. */\nexport function needsRepoFacts(\n\tcustomization: OidcSubjectCustomization,\n): boolean {\n\tconst immutableWithoutPrefix =\n\t\tcustomization.use_immutable_subject === true &&\n\t\t!customization.sub_claim_prefix;\n\tif (customization.use_default) return immutableWithoutPrefix;\n\n\tconst keys = customization.include_claim_keys ?? [];\n\treturn keys.some(\n\t\t(key) => NEEDS_FACTS.has(key) || (key === 'repo' && immutableWithoutPrefix),\n\t);\n}\n\n/**\n * The exact `sub` of a job in `repo`'s `stage` environment.\n *\n * @throws OidcSubjectNotSupported when a custom template includes a claim\n * whose value depends on the run, so no one subject can be trusted.\n */\nexport function oidcSubject(\n\trepo: string,\n\tstage: string,\n\tcustomization: OidcSubjectCustomization,\n\tfacts?: RepoFacts,\n): { subject: string; kind: OidcSubjectKind } {\n\tconst [owner = '', name = ''] = repo.split('/');\n\tconst environment = `environment:${claim(stage)}`;\n\n\tconst repoSegment = (): string => {\n\t\tif (customization.sub_claim_prefix) return customization.sub_claim_prefix;\n\t\tif (customization.use_immutable_subject) {\n\t\t\tif (!facts) throw new RepoFactsRequired(repo);\n\t\t\treturn `repo:${owner}@${facts.ownerId}/${name}@${facts.repoId}`;\n\t\t}\n\t\treturn `repo:${repo}`;\n\t};\n\tconst immutable =\n\t\tcustomization.use_immutable_subject === true ||\n\t\tcustomization.sub_claim_prefix !== undefined;\n\n\tif (customization.use_default) {\n\t\treturn {\n\t\t\tsubject: `${repoSegment()}:${environment}`,\n\t\t\tkind: immutable ? 'immutable' : 'default',\n\t\t};\n\t}\n\n\tconst keys = customization.include_claim_keys ?? [];\n\tconst unsupported = keys.filter(\n\t\t(key) => !(SUPPORTED_CLAIM_KEYS as readonly string[]).includes(key),\n\t);\n\tif (keys.length === 0 || unsupported.length > 0) {\n\t\tthrow new OidcSubjectNotSupported(repo, keys, unsupported);\n\t}\n\tif (keys.some((key) => NEEDS_FACTS.has(key)) && !facts) {\n\t\tthrow new RepoFactsRequired(repo);\n\t}\n\n\tconst parts = keys.map((key) => {\n\t\tswitch (key) {\n\t\t\tcase 'repo':\n\t\t\t\treturn repoSegment();\n\t\t\tcase 'context':\n\t\t\tcase 'environment':\n\t\t\t\treturn environment;\n\t\t\tcase 'repository':\n\t\t\t\treturn `repository:${claim(repo)}`;\n\t\t\tcase 'repository_owner':\n\t\t\t\treturn `repository_owner:${claim(owner)}`;\n\t\t\tcase 'repository_owner_id':\n\t\t\t\treturn `repository_owner_id:${facts!.ownerId}`;\n\t\t\tcase 'repository_id':\n\t\t\t\treturn `repository_id:${facts!.repoId}`;\n\t\t\tdefault:\n\t\t\t\treturn `repository_visibility:${claim(facts!.visibility)}`;\n\t\t}\n\t});\n\treturn { subject: parts.join(':'), kind: 'custom' };\n}\n\n/** What {@link resolveOidcSubject} found. */\nexport interface ResolvedOidcSubject {\n\tsubject: string;\n\tkind: OidcSubjectKind;\n\t/** Set when the format could not be read and the default was assumed. */\n\twarning?: string;\n}\n\n/** The endpoint a repository's subject format is read from. */\nexport function customizationEndpoint(repo: string): string {\n\treturn `repos/${repo}/actions/oidc/customization/sub`;\n}\n\n/**\n * The `sub` GitHub will send for `repo`'s `stage` environment, read from the\n * repository's OIDC settings through `gh`.\n *\n * A repository on a custom template that names no keys of its own uses its\n * organisation's, read from `orgs/{owner}/actions/oidc/customization/sub`.\n * When the settings cannot be read at all — no permission, or a GitHub\n * Enterprise Server without the endpoint — the default format is assumed, with\n * a warning saying where to check it.\n */\nexport function resolveOidcSubject(\n\tgh: Gh,\n\trepo: string,\n\tstage: string,\n): ResolvedOidcSubject {\n\tconst endpoint = customizationEndpoint(repo);\n\tlet customization: OidcSubjectCustomization;\n\ttry {\n\t\tcustomization = JSON.parse(gh(['api', endpoint]));\n\t} catch {\n\t\treturn {\n\t\t\tsubject: `repo:${repo}:environment:${claim(stage)}`,\n\t\t\tkind: 'assumed',\n\t\t\twarning: `Could not read ${endpoint}, so the default subject format is assumed. Check it with: gh api ${endpoint} — a repository on the immutable subject or a custom template sends a different sub, and the role would not be assumable.`,\n\t\t};\n\t}\n\n\tif (\n\t\t!customization.use_default &&\n\t\t(customization.include_claim_keys ?? []).length === 0\n\t) {\n\t\t// The repository follows its organisation's template.\n\t\tconst [owner = ''] = repo.split('/');\n\t\tconst orgEndpoint = `orgs/${owner}/actions/oidc/customization/sub`;\n\t\tlet org: { include_claim_keys?: string[]; use_immutable_subject?: boolean };\n\t\ttry {\n\t\t\torg = JSON.parse(gh(['api', orgEndpoint]));\n\t\t} catch {\n\t\t\tthrow new OrgOidcTemplateUnreadable(repo, orgEndpoint);\n\t\t}\n\t\tcustomization = {\n\t\t\t...customization,\n\t\t\tinclude_claim_keys: org.include_claim_keys ?? [],\n\t\t\tuse_immutable_subject:\n\t\t\t\tcustomization.use_immutable_subject ?? org.use_immutable_subject,\n\t\t};\n\t}\n\n\tconst facts = needsRepoFacts(customization)\n\t\t? readRepoFacts(gh, repo)\n\t\t: undefined;\n\treturn oidcSubject(repo, stage, customization, facts);\n}\n\nfunction readRepoFacts(gh: Gh, repo: string): RepoFacts {\n\tconst raw = JSON.parse(gh(['api', `repos/${repo}`])) as {\n\t\tid: number;\n\t\towner: { id: number };\n\t\tvisibility: string;\n\t};\n\treturn {\n\t\townerId: raw.owner.id,\n\t\trepoId: raw.id,\n\t\tvisibility: raw.visibility,\n\t};\n}\n\n/**\n * The repository's custom subject template includes a claim whose value\n * depends on the run — or names none — so no one subject can be trusted.\n */\nexport class OidcSubjectNotSupported extends GkmError {\n\tconstructor(\n\t\treadonly repo: string,\n\t\treadonly includeClaimKeys: string[],\n\t\treadonly unsupportedKeys: string[],\n\t) {\n\t\tsuper(\n\t\t\t`${repo} builds its OIDC subject from a custom template, ${JSON.stringify(includeClaimKeys)}, and ${\n\t\t\t\tunsupportedKeys.length > 0\n\t\t\t\t\t? `${unsupportedKeys.join(', ')} depend${unsupportedKeys.length === 1 ? 's' : ''} on the run`\n\t\t\t\t\t: 'it names no claims'\n\t\t\t}, so the deploy role cannot trust one exact subject. Set the repository back to the default subject — gh api --method PUT ${customizationEndpoint(repo)} -F use_default=true — (or keep the immutable one by also passing -F use_immutable_subject=true), or use a template built only from ${SUPPORTED_CLAIM_KEYS.join(', ')}; then run this again.`,\n\t\t);\n\t\tthis.name = 'OidcSubjectNotSupported';\n\t}\n}\n\n/** The subject needs the repository's ids, and none were read. */\nexport class RepoFactsRequired extends GkmError {\n\tconstructor(readonly repo: string) {\n\t\tsuper(\n\t\t\t`The OIDC subject of ${repo} includes its owner and repository ids; read them with gh api repos/${repo} and pass them in.`,\n\t\t);\n\t\tthis.name = 'RepoFactsRequired';\n\t}\n}\n\n/**\n * The repository uses its organisation's subject template, and that template\n * could not be read — so what GitHub sends is unknown, and is not guessed.\n */\nexport class OrgOidcTemplateUnreadable extends GkmError {\n\tconstructor(\n\t\treadonly repo: string,\n\t\treadonly endpoint: string,\n\t) {\n\t\tsuper(\n\t\t\t`${repo} uses its organisation's OIDC subject template, and ${endpoint} could not be read. Run this as an organisation owner (gh auth refresh -s admin:org), or set the repository back to the default subject with gh api --method PUT ${customizationEndpoint(repo)} -F use_default=true; then run this again.`,\n\t\t);\n\t\tthis.name = 'OrgOidcTemplateUnreadable';\n\t}\n}\n","/**\n * What the deploy role may do in the stage's account.\n *\n * An SST stage creates its own stack, which is most of AWS, so its role gets\n * `AdministratorAccess`. A stage deployed with the `compose` target builds and\n * runs containers on a server; from AWS its deploy job needs only the stage's\n * secrets — read, and written back when a deploy generates a new one — and,\n * when the deploy state is kept in AWS, the stage's state, and, when the\n * stage's domain is in Route53, the records of its hosted zone, and, when an\n * `s3` provider backs its buckets, those buckets and the IAM user each is\n * reached with — by the names the provider gives them. So a compose stage\n * gets an inline policy naming exactly those resources and nothing else.\n *\n * `--policy-arn` replaces either.\n */\n\nimport { stageDnsDomains } from '../compose/dns.js';\nimport { stageProvider } from '../providers/config.js';\nimport {\n\tPROJECT_BUCKET_CREATE_ACTIONS,\n\tprojectBucketName,\n} from '../providers/projectBucket.js';\nimport {\n\tbucketName,\n\tIAM_USER_PATH,\n\tiamUserName,\n\tSUFFIX_LENGTH,\n} from '../providers/s3/naming.js';\nimport { secretsParameterName } from '../secrets/aws.js';\nimport { secretsManagerSecretName } from '../secrets/secretsManager.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { deployIdentity } from './identity.js';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default\n * outside compose — said out loud in the output, and replaceable with\n * `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\n/** The name of the inline policy a compose stage's role is given. */\nexport const SCOPED_POLICY_NAME = 'gkm-deploy';\n\n/** One statement of an IAM policy document. */\nexport interface PolicyStatement {\n\tSid: string;\n\tEffect: 'Allow';\n\tAction: string[];\n\tResource: string | string[];\n\tCondition?: Record<string, Record<string, string>>;\n}\n\n/** What the role is given. */\nexport type DeployAccess =\n\t| {\n\t\t\tkind: 'managed';\n\t\t\tpolicyArn: string;\n\t\t\t/** Why, for the output. */\n\t\t\treason: string;\n\t }\n\t| {\n\t\t\tkind: 'scoped';\n\t\t\t/** The statements, in the account the role is in. */\n\t\t\tstatements(account: string): PolicyStatement[];\n\t\t\t/** What each statement is for, for the output. */\n\t\t\tdescribe: string[];\n\t };\n\n/** Every target the stage's apps deploy through. */\nexport function deployTargets(workspace: NormalizedWorkspace): string[] {\n\tconst targets = new Set<string>([workspace.deploy?.default ?? 'dokploy']);\n\tfor (const app of Object.values(workspace.apps)) {\n\t\ttargets.add(app.resolvedDeployTarget);\n\t}\n\treturn [...targets].sort();\n}\n\n/**\n * The access the stage's deploy role is given: `--policy-arn` when passed,\n * the scoped policy for a stage only the compose target deploys, and\n * `AdministratorAccess` otherwise.\n */\nexport function deployAccess(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\tpolicyArn?: string,\n\t/** The ids of the stage's `ObjectStorage` constructs, from its manifest. */\n\tbuckets: readonly string[] = [],\n): DeployAccess {\n\tif (policyArn) {\n\t\treturn { kind: 'managed', policyArn, reason: '--policy-arn' };\n\t}\n\n\tconst targets = deployTargets(workspace);\n\tif (targets.length !== 1 || targets[0] !== 'compose') {\n\t\treturn {\n\t\t\tkind: 'managed',\n\t\t\tpolicyArn: DEFAULT_POLICY_ARN,\n\t\t\treason: `the ${targets.join(', ')} target${targets.length === 1 ? '' : 's'} deploy${targets.length === 1 ? 's' : ''} infrastructure; --policy-arn to narrow`,\n\t\t};\n\t}\n\n\tconst project = workspace.name;\n\tconst store = workspace.secrets.store ?? 'file';\n\tconst state = workspace.state;\n\tconst custom =\n\t\t(typeof store === 'object' && typeof store.provider === 'object') ||\n\t\t(state !== undefined && typeof state.provider === 'object');\n\tif (custom) {\n\t\treturn {\n\t\t\tkind: 'managed',\n\t\t\tpolicyArn: DEFAULT_POLICY_ARN,\n\t\t\treason: `a custom ${typeof store === 'object' && typeof store.provider === 'object' ? 'secrets store' : 'state provider'} cannot be scoped; --policy-arn to narrow`,\n\t\t};\n\t}\n\n\tconst describe: string[] = [];\n\tconst build: ((account: string) => PolicyStatement[])[] = [];\n\n\tif (typeof store === 'object' && store.provider === 'ssm') {\n\t\tconst { region } = store;\n\t\tconst name = secretsParameterName(project, stage);\n\t\tdescribe.push(`read and update ${name} (SSM, ${region})`);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageSecrets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['ssm:GetParameter', 'ssm:PutParameter'],\n\t\t\t\tResource: `arn:aws:ssm:${region}:${account}:parameter${name}`,\n\t\t\t},\n\t\t]);\n\t} else if (\n\t\ttypeof store === 'object' &&\n\t\tstore.provider === 'secrets-manager'\n\t) {\n\t\tconst { region, kmsKeyId } = store as { region: string; kmsKeyId?: string };\n\t\tconst name = secretsManagerSecretName(project, stage);\n\t\tdescribe.push(`read and update ${name} (Secrets Manager, ${region})`);\n\t\tbuild.push((account) => {\n\t\t\tconst statements: PolicyStatement[] = [\n\t\t\t\t{\n\t\t\t\t\tSid: 'StageSecrets',\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tAction: [\n\t\t\t\t\t\t'secretsmanager:GetSecretValue',\n\t\t\t\t\t\t'secretsmanager:PutSecretValue',\n\t\t\t\t\t\t'secretsmanager:CreateSecret',\n\t\t\t\t\t],\n\t\t\t\t\t// Secrets Manager appends six random characters to a secret's ARN.\n\t\t\t\t\tResource: `arn:aws:secretsmanager:${region}:${account}:secret:${name}-??????`,\n\t\t\t\t},\n\t\t\t];\n\t\t\tif (kmsKeyId) {\n\t\t\t\tstatements.push({\n\t\t\t\t\tSid: 'StageSecretsKey',\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tAction: ['kms:Decrypt', 'kms:GenerateDataKey'],\n\t\t\t\t\tResource: kmsKeyId.startsWith('arn:') ? kmsKeyId : '*',\n\t\t\t\t\tCondition: {\n\t\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t\t'kms:ViaService': `secretsmanager.${region}.amazonaws.com`,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn statements;\n\t\t});\n\t}\n\n\tif (state?.provider === 'ssm') {\n\t\tconst { region } = state;\n\t\tconst path = `/gkm/${project}/${stage}/`;\n\t\tdescribe.push(\n\t\t\t`read and write the deploy state under ${path} (SSM, ${region})`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageState',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['ssm:GetParameter', 'ssm:PutParameter', 'ssm:DeleteParameter'],\n\t\t\t\tResource: `arn:aws:ssm:${region}:${account}:parameter${path}*`,\n\t\t\t},\n\t\t]);\n\t} else if (state?.provider === 's3') {\n\t\tconst prefix = (state.prefix ?? 'gkm').replace(/\\/+$/, '');\n\t\tconst path = `${prefix ? `${prefix}/` : ''}${project}/${stage}/`;\n\t\tconst named = state.bucket;\n\t\t// No bucket named: the project bucket, which the first deploy creates\n\t\t// — by a name the account fixes, so the role is given exactly it.\n\t\tconst bucketIn = (account: string) =>\n\t\t\tnamed ??\n\t\t\tprojectBucketName(deployIdentity(workspace, stage).scope, account);\n\t\tdescribe.push(\n\t\t\tnamed\n\t\t\t\t? `read and write the deploy state under s3://${named}/${path}`\n\t\t\t\t: `create the project bucket ${projectBucketName(deployIdentity(workspace, stage).scope, '<account>')}, and read and write the deploy state under ${path} in it`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t...stateBucketStatements(bucketIn(account), path),\n\t\t\t...(named\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSid: 'ProjectBucket',\n\t\t\t\t\t\t\tEffect: 'Allow' as const,\n\t\t\t\t\t\t\tAction: [...PROJECT_BUCKET_CREATE_ACTIONS],\n\t\t\t\t\t\t\tResource: `arn:aws:s3:::${bucketIn(account)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]),\n\t\t]);\n\t}\n\n\t// The deploy creates each bucket an s3 provider backs, and the IAM user\n\t// and key it is reached with: by their names — the good one, or the good\n\t// one with a clash's six-character suffix — and nothing else.\n\tconst objects = stageProvider(workspace, 'objects', stage);\n\tif (objects.mode === 'provider' && objects.name === 's3' && buckets.length) {\n\t\tconst { scope } = deployIdentity(workspace, stage);\n\t\tconst names = [...buckets].sort().map((id) => ({\n\t\t\tbucket: bucketName({ scope, stage, id }),\n\t\t\tuser: iamUserName({ scope, stage, id }),\n\t\t}));\n\t\tconst suffix = '?'.repeat(SUFFIX_LENGTH - 1);\n\t\tdescribe.push(\n\t\t\t`create and configure the buckets ${names.map((n) => n.bucket).join(', ')} and their IAM users (S3, IAM)`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageBuckets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:CreateBucket',\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t's3:GetBucketTagging',\n\t\t\t\t\t's3:PutBucketTagging',\n\t\t\t\t\t's3:GetBucketPublicAccessBlock',\n\t\t\t\t\t's3:PutBucketPublicAccessBlock',\n\t\t\t\t\t's3:GetBucketPolicy',\n\t\t\t\t\t's3:PutBucketPolicy',\n\t\t\t\t\t's3:GetEncryptionConfiguration',\n\t\t\t\t\t's3:PutEncryptionConfiguration',\n\t\t\t\t\t's3:GetBucketVersioning',\n\t\t\t\t\t's3:PutBucketVersioning',\n\t\t\t\t\t's3:GetBucketCORS',\n\t\t\t\t\t's3:PutBucketCORS',\n\t\t\t\t],\n\t\t\t\tResource: names.flatMap((n) => [\n\t\t\t\t\t`arn:aws:s3:::${n.bucket}`,\n\t\t\t\t\t`arn:aws:s3:::${n.bucket}-${suffix}`,\n\t\t\t\t]),\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageBucketUsers',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'iam:GetUser',\n\t\t\t\t\t'iam:CreateUser',\n\t\t\t\t\t'iam:TagUser',\n\t\t\t\t\t'iam:ListUserTags',\n\t\t\t\t\t'iam:GetUserPolicy',\n\t\t\t\t\t'iam:PutUserPolicy',\n\t\t\t\t\t'iam:ListAccessKeys',\n\t\t\t\t\t'iam:CreateAccessKey',\n\t\t\t\t\t'iam:DeleteAccessKey',\n\t\t\t\t],\n\t\t\t\tResource: names.map(\n\t\t\t\t\t(n) => `arn:aws:iam::${account}:user${IAM_USER_PATH}${n.user}`,\n\t\t\t\t),\n\t\t\t},\n\t\t]);\n\t}\n\n\t// The deploy writes the stage's hosts' records: in Route53, only that\n\t// zone's — and, with no hostedZoneId to name it, the zone is looked up\n\t// by name, which IAM cannot scope.\n\tfor (const { domain, config } of stageDnsDomains(\n\t\tstage,\n\t\tworkspace.domains,\n\t\tworkspace.dns,\n\t)) {\n\t\tif (config.provider !== 'route53') continue;\n\t\tconst zoneId = (config as { hostedZoneId?: string }).hostedZoneId?.replace(\n\t\t\t/^\\/?hostedzone\\//,\n\t\t\t'',\n\t\t);\n\t\tdescribe.push(\n\t\t\t`write the A, AAAA and CNAME records of ${domain} (Route53${zoneId ? `, ${zoneId}` : ''})`,\n\t\t);\n\t\tbuild.push(() => [\n\t\t\t{\n\t\t\t\tSid: 'StageDnsRecords',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'route53:ChangeResourceRecordSets',\n\t\t\t\t\t'route53:ListResourceRecordSets',\n\t\t\t\t],\n\t\t\t\tResource: `arn:aws:route53:::hostedzone/${zoneId ?? '*'}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageDnsChanges',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['route53:GetChange'],\n\t\t\t\tResource: 'arn:aws:route53:::change/*',\n\t\t\t},\n\t\t\t...(zoneId\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSid: 'StageDnsZones',\n\t\t\t\t\t\t\tEffect: 'Allow' as const,\n\t\t\t\t\t\t\tAction: ['route53:ListHostedZonesByName'],\n\t\t\t\t\t\t\tResource: '*',\n\t\t\t\t\t\t},\n\t\t\t\t\t]),\n\t\t]);\n\t}\n\n\treturn {\n\t\tkind: 'scoped',\n\t\tstatements: (account) => build.flatMap((statements) => statements(account)),\n\t\tdescribe,\n\t};\n}\n\n/**\n * Reading and writing one stage's deploy state in `bucket`, as the deploy\n * role is given it.\n */\nexport function stateBucketStatements(\n\tbucket: string,\n\tpath: string,\n): PolicyStatement[] {\n\treturn [\n\t\t{\n\t\t\tSid: 'StageState',\n\t\t\tEffect: 'Allow',\n\t\t\tAction: ['s3:GetObject', 's3:PutObject', 's3:DeleteObject'],\n\t\t\tResource: `arn:aws:s3:::${bucket}/${path}*`,\n\t\t},\n\t\t// HeadBucket, and a state not written yet a 404 rather than a 403.\n\t\t{\n\t\t\tSid: 'StageStateBucket',\n\t\t\tEffect: 'Allow',\n\t\t\tAction: ['s3:ListBucket'],\n\t\t\tResource: `arn:aws:s3:::${bucket}`,\n\t\t},\n\t];\n}\n\n/** The scoped access as a policy document, or null when it allows nothing. */\nexport function scopedPolicyDocument(\n\tstatements: PolicyStatement[],\n): string | null {\n\tif (statements.length === 0) return null;\n\treturn JSON.stringify({ Version: '2012-10-17', Statement: statements });\n}\n\n/** The account an IAM ARN is in: `arn:aws:iam::<account>:…`. */\nexport function accountOf(arn: string): string {\n\treturn arn.split(':')[4] ?? '';\n}\n","/**\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 or Secrets Manager the deploy job reads them with the role instead, so\n * no key is handed to GitHub, and this checks the stage's store in the same\n * account, with the same profile. Re-running it converges rather than duplicating.\n *\n * The role trusts the exact `sub` GitHub sends for the environment, read from\n * the repository's OIDC subject settings (`githubOidc.ts`) — the default, the\n * immutable subject with the owner's and repository's ids, or a custom\n * template. A re-run rewrites an existing role's trust to it, so running this\n * again repairs a role that trusted the wrong format. What the role may do is\n * `githubPolicy.ts`: `AdministratorAccess` for SST, and for a compose stage\n * only its own secrets (and deploy state, when that is in AWS).\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tDeleteRolePolicyCommand,\n\tDetachRolePolicyCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListAttachedRolePoliciesCommand,\n\tListOpenIDConnectProvidersCommand,\n\tListRoleTagsCommand,\n\tPutRolePolicyCommand,\n\tTagRoleCommand,\n\tUntagRoleCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { GkmError } from '../errors';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { getKeyPath, keystoreProject, readKey } from '../secrets/keystore.js';\nimport { isRemoteStore, secretsStoreFor } from '../secrets/store.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport { resolveOidcSubject } from './githubOidc.js';\nimport {\n\taccountOf,\n\tdeployAccess,\n\tSCOPED_POLICY_NAME,\n\tscopedPolicyDocument,\n} from './githubPolicy.js';\n\nexport { DEFAULT_POLICY_ARN, SCOPED_POLICY_NAME } from './githubPolicy.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * The tag recording which managed policy gkm attached to the role — so a\n * re-run detaches only what gkm put there, never a policy someone else did.\n */\nexport const POLICY_TAG = 'gkm:policy-arn';\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, in place of the default. */\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// stderr captured, so a read that fails (a 404) is not printed as noise.\n\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t...(input === undefined ? {} : { input }),\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 whose OIDC token carries exactly\n * `subject` — the repository's `<stage>` environment, in the format the\n * repository sends (see `githubOidc.ts`), and nothing else. Not another\n * branch's job, not another environment's: that is what keeps a staging\n * deploy from using the production role.\n */\nexport function trustPolicy(providerArn: string, subject: string): 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`]: subject,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/**\n * The subject a trust policy trusts, or undefined when it names none. IAM\n * hands documents back URL-encoded.\n */\nexport function trustedSubject(\n\tdocument: string | undefined,\n): string | undefined {\n\tif (!document) return undefined;\n\tlet parsed: {\n\t\tStatement?: { Condition?: Record<string, Record<string, unknown>> }[];\n\t};\n\ttry {\n\t\tparsed = JSON.parse(safeDecode(document));\n\t} catch {\n\t\treturn undefined;\n\t}\n\tfor (const statement of parsed.Statement ?? []) {\n\t\tfor (const operator of Object.values(statement.Condition ?? {})) {\n\t\t\tconst value = operator[`${GITHUB_OIDC_HOST}:sub`];\n\t\t\tif (typeof value === 'string') return value;\n\t\t\tif (Array.isArray(value)) return value.join(', ');\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction safeDecode(value: string): string {\n\ttry {\n\t\treturn decodeURIComponent(value);\n\t} catch {\n\t\treturn value;\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/**\n * What the role is given: a managed policy, attached; or an inline policy\n * document — null for none at all.\n */\nexport type RoleAccess = { managed: string } | { inline: string | null };\n\n/** What {@link ensureRole} did. */\nexport interface RoleOutcome {\n\tarn: string;\n\tcreated: boolean;\n\t/** The subject the role trusted before, when it existed. */\n\tpreviousSubject?: string;\n\t/** Managed policies gkm had attached and detached now. */\n\tdetached: string[];\n\t/**\n\t * Managed policies still attached that the role no longer needs, which gkm\n\t * did not record attaching — left alone, for a person to detach.\n\t */\n\tleftAttached: string[];\n}\n\nfunction isNoSuchEntity(error: unknown): boolean {\n\treturn (error as { name?: string }).name === 'NoSuchEntityException';\n}\n\n/**\n * The deploy role, created or brought back to this trust and access.\n *\n * Converges: re-running rewrites the trust, and moves the role from one access\n * to another — detaching a managed policy only when the role's\n * {@link POLICY_TAG} says gkm attached it.\n */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\taccess: RoleAccess,\n): Promise<RoleOutcome> {\n\tlet arn: string;\n\tlet created = false;\n\tlet previousSubject: string | undefined;\n\tlet recorded: string | undefined;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tpreviousSubject = trustedSubject(Role!.AssumeRolePolicyDocument);\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\tconst { Tags = [] } = await iam.send(\n\t\t\tnew ListRoleTagsCommand({ RoleName: name }),\n\t\t);\n\t\trecorded = Tags.find((tag) => tag.Key === POLICY_TAG)?.Value;\n\t} catch (error) {\n\t\tif (!isNoSuchEntity(error)) throw error;\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\tcreated = true;\n\t}\n\n\tconst detached: string[] = [];\n\tconst leftAttached: string[] = [];\n\tconst detach = async (policyArn: string) => {\n\t\ttry {\n\t\t\tawait iam.send(\n\t\t\t\tnew DetachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t\t\t);\n\t\t\tdetached.push(policyArn);\n\t\t} catch (error) {\n\t\t\t// Already gone — by hand, or a run that stopped halfway.\n\t\t\tif (!isNoSuchEntity(error)) throw error;\n\t\t}\n\t};\n\tconst deleteInline = async () => {\n\t\ttry {\n\t\t\tawait iam.send(\n\t\t\t\tnew DeleteRolePolicyCommand({\n\t\t\t\t\tRoleName: name,\n\t\t\t\t\tPolicyName: SCOPED_POLICY_NAME,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!isNoSuchEntity(error)) throw error;\n\t\t}\n\t};\n\n\tif ('managed' in access) {\n\t\t// Attaching an attached policy is a no-op, so this converges too.\n\t\tawait iam.send(\n\t\t\tnew AttachRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyArn: access.managed,\n\t\t\t}),\n\t\t);\n\t\tif (recorded && recorded !== access.managed) await detach(recorded);\n\t\tif (recorded !== access.managed) {\n\t\t\tawait iam.send(\n\t\t\t\tnew TagRoleCommand({\n\t\t\t\t\tRoleName: name,\n\t\t\t\t\tTags: [{ Key: POLICY_TAG, Value: access.managed }],\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (!created) await deleteInline();\n\t\treturn { arn, created, previousSubject, detached, leftAttached };\n\t}\n\n\tif (access.inline) {\n\t\tawait iam.send(\n\t\t\tnew PutRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyName: SCOPED_POLICY_NAME,\n\t\t\t\tPolicyDocument: access.inline,\n\t\t\t}),\n\t\t);\n\t} else if (!created) {\n\t\tawait deleteInline();\n\t}\n\tif (created) return { arn, created, previousSubject, detached, leftAttached };\n\n\tif (recorded) {\n\t\tawait detach(recorded);\n\t\tawait iam.send(\n\t\t\tnew UntagRoleCommand({ RoleName: name, TagKeys: [POLICY_TAG] }),\n\t\t);\n\t}\n\t// A role from before gkm recorded what it attached: what is attached may\n\t// be gkm's or someone's own, so it is named, not detached.\n\tconst { AttachedPolicies = [] } = await iam.send(\n\t\tnew ListAttachedRolePoliciesCommand({ RoleName: name }),\n\t);\n\tfor (const policy of AttachedPolicies) {\n\t\tif (policy.PolicyArn && !detached.includes(policy.PolicyArn)) {\n\t\t\tleftAttached.push(policy.PolicyArn);\n\t\t}\n\t}\n\treturn { arn, created, previousSubject, detached, leftAttached };\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; subject: 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\t// The exact subject GitHub will send — read from the repository's OIDC\n\t// settings, before anything is changed, so a format no role can trust\n\t// fails here.\n\tconst oidc = resolveOidcSubject(gh, repo, options.stage);\n\t// The buckets an s3 provider creates are named after their constructs,\n\t// so the role is scoped to exactly those names.\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t});\n\tconst buckets = Object.entries(manifest)\n\t\t.filter(([, d]) => d.kind === 'objects')\n\t\t.map(([id]) => id);\n\tconst access = deployAccess(\n\t\tworkspace,\n\t\toptions.stage,\n\t\toptions.policyArn,\n\t\tbuckets,\n\t);\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\tif (access.kind === 'managed') {\n\t\tlogger.log(` Policy: ${access.policyArn} (${access.reason})`);\n\t} else if (access.describe.length > 0) {\n\t\tlogger.log(\n\t\t\t` Policy: inline \"${SCOPED_POLICY_NAME}\", scoped to the compose deploy — only:`,\n\t\t);\n\t\tfor (const line of access.describe) logger.log(` ${line}`);\n\t} else {\n\t\tlogger.log(\n\t\t\t' Policy: none — a compose deploy with file secrets and local state reads nothing from AWS',\n\t\t);\n\t}\n\tlogger.log(\n\t\t` Trusted by: ${oidc.subject} only (${SUBJECT_KINDS[oidc.kind]})`,\n\t);\n\tif (oidc.warning) logger.log(` ⚠ ${oidc.warning}`);\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\tif (access.kind === 'scoped') {\n\t\t\tconst document = scopedPolicyDocument(access.statements('<account>'));\n\t\t\tif (document) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`\\n Inline policy:\\n${JSON.stringify(JSON.parse(document), null, 2).replace(/^/gm, ' ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo, subject: oidc.subject };\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 outcome = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, oidc.subject),\n\t\taccess.kind === 'managed'\n\t\t\t? { managed: access.policyArn }\n\t\t\t: {\n\t\t\t\t\tinline: scopedPolicyDocument(\n\t\t\t\t\t\taccess.statements(accountOf(providerArn)),\n\t\t\t\t\t),\n\t\t\t\t},\n\t);\n\tconst roleArn = outcome.arn;\n\tlogger.log(`\\n ✓ AWS: ${roleArn}${outcome.created ? ' (created)' : ''}`);\n\tif (!outcome.created) {\n\t\tlogger.log(\n\t\t\toutcome.previousSubject === oidc.subject\n\t\t\t\t? ` ✓ Trust: unchanged, ${oidc.subject}`\n\t\t\t\t: ` ✓ Trust: ${outcome.previousSubject ?? '(no subject)'} → ${oidc.subject}`,\n\t\t);\n\t}\n\tfor (const detached of outcome.detached) {\n\t\tlogger.log(` ✓ Detached ${detached} (attached by gkm, no longer needed)`);\n\t}\n\tif (access.kind === 'scoped') {\n\t\tfor (const attached of outcome.leftAttached) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ ${attached} is still attached to ${role}. gkm has no record of attaching it, so it is left; the compose deploy does not need it. Detach it with: aws iam detach-role-policy --role-name ${role} --policy-arn ${attached}${options.profile ? ` --profile ${options.profile}` : ''}`,\n\t\t\t);\n\t\t}\n\t}\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, subject: oidc.subject };\n}\n\nconst SUBJECT_KINDS = {\n\tdefault: \"the repository's default subject\",\n\timmutable: \"the repository's immutable subject\",\n\tcustom: \"the repository's custom subject template\",\n\tassumed: 'assumed: the default format',\n} as const;\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends GkmError {\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 +0,0 @@
1
- {"version":3,"file":"github-zgaBOQvh.mjs","names":[],"sources":["../src/deploy/githubOidc.ts","../src/deploy/githubPolicy.ts","../src/deploy/github.ts"],"sourcesContent":["/**\n * The `sub` GitHub puts in a deploy job's OIDC token — what the deploy role's\n * trust policy has to match exactly.\n *\n * It is not always `repo:<owner>/<name>:environment:<stage>`. A repository\n * (or its organisation) decides the format, and says which through\n * `GET /repos/{owner}/{repo}/actions/oidc/customization/sub`:\n *\n * - the default: `repo:<owner>/<name>:environment:<stage>`;\n * - the immutable subject — the default for repositories created after\n * 15 July 2026, opt-in before — puts the owner's and the repository's ids in\n * the `repo` segment: `repo:<owner>@<ownerId>/<name>@<repoId>:environment:<stage>`.\n * The API hands that segment back as `sub_claim_prefix`;\n * - a custom template (`use_default: false`) builds the `sub` from\n * `include_claim_keys`, in order, each as `<key>:<value>` joined by `:`,\n * where `context` is `environment:<stage>` in an environment job.\n *\n * A `:` inside a value is sent as `%3A`.\n *\n * See https://docs.github.com/en/actions/reference/security/oidc.\n */\n\nimport { GkmError } from '../errors';\nimport type { Gh } from './github.js';\n\n/** `GET /repos/{owner}/{repo}/actions/oidc/customization/sub`. */\nexport interface OidcSubjectCustomization {\n\tuse_default: boolean;\n\tinclude_claim_keys?: string[];\n\tuse_immutable_subject?: boolean;\n\t/** The `repo` segment of the subject, as GitHub will send it. */\n\tsub_claim_prefix?: string;\n}\n\n/** What `GET /repos/{owner}/{repo}` says that a subject may include. */\nexport interface RepoFacts {\n\townerId: number;\n\trepoId: number;\n\tvisibility: string;\n}\n\n/**\n * Template keys whose values are known for a repository's environment job\n * before the job runs. Anything else — `job_workflow_ref`, `ref`, `sha`,\n * `run_id`, `workflow`, `actor`, a custom property — depends on the run.\n */\nexport const SUPPORTED_CLAIM_KEYS = [\n\t'repo',\n\t'context',\n\t'environment',\n\t'repository',\n\t'repository_owner',\n\t'repository_owner_id',\n\t'repository_id',\n\t'repository_visibility',\n] as const;\n\nconst NEEDS_FACTS = new Set([\n\t'repository_owner_id',\n\t'repository_id',\n\t'repository_visibility',\n]);\n\n/** A `:` inside a claim value is sent as `%3A`. */\nfunction claim(value: string): string {\n\treturn value.replaceAll(':', '%3A');\n}\n\n/** Which kind of subject the repository uses, for the output. */\nexport type OidcSubjectKind = 'default' | 'immutable' | 'custom' | 'assumed';\n\n/** Whether {@link oidcSubject} needs the repository's ids or visibility. */\nexport function needsRepoFacts(\n\tcustomization: OidcSubjectCustomization,\n): boolean {\n\tconst immutableWithoutPrefix =\n\t\tcustomization.use_immutable_subject === true &&\n\t\t!customization.sub_claim_prefix;\n\tif (customization.use_default) return immutableWithoutPrefix;\n\n\tconst keys = customization.include_claim_keys ?? [];\n\treturn keys.some(\n\t\t(key) => NEEDS_FACTS.has(key) || (key === 'repo' && immutableWithoutPrefix),\n\t);\n}\n\n/**\n * The exact `sub` of a job in `repo`'s `stage` environment.\n *\n * @throws OidcSubjectNotSupported when a custom template includes a claim\n * whose value depends on the run, so no one subject can be trusted.\n */\nexport function oidcSubject(\n\trepo: string,\n\tstage: string,\n\tcustomization: OidcSubjectCustomization,\n\tfacts?: RepoFacts,\n): { subject: string; kind: OidcSubjectKind } {\n\tconst [owner = '', name = ''] = repo.split('/');\n\tconst environment = `environment:${claim(stage)}`;\n\n\tconst repoSegment = (): string => {\n\t\tif (customization.sub_claim_prefix) return customization.sub_claim_prefix;\n\t\tif (customization.use_immutable_subject) {\n\t\t\tif (!facts) throw new RepoFactsRequired(repo);\n\t\t\treturn `repo:${owner}@${facts.ownerId}/${name}@${facts.repoId}`;\n\t\t}\n\t\treturn `repo:${repo}`;\n\t};\n\tconst immutable =\n\t\tcustomization.use_immutable_subject === true ||\n\t\tcustomization.sub_claim_prefix !== undefined;\n\n\tif (customization.use_default) {\n\t\treturn {\n\t\t\tsubject: `${repoSegment()}:${environment}`,\n\t\t\tkind: immutable ? 'immutable' : 'default',\n\t\t};\n\t}\n\n\tconst keys = customization.include_claim_keys ?? [];\n\tconst unsupported = keys.filter(\n\t\t(key) => !(SUPPORTED_CLAIM_KEYS as readonly string[]).includes(key),\n\t);\n\tif (keys.length === 0 || unsupported.length > 0) {\n\t\tthrow new OidcSubjectNotSupported(repo, keys, unsupported);\n\t}\n\tif (keys.some((key) => NEEDS_FACTS.has(key)) && !facts) {\n\t\tthrow new RepoFactsRequired(repo);\n\t}\n\n\tconst parts = keys.map((key) => {\n\t\tswitch (key) {\n\t\t\tcase 'repo':\n\t\t\t\treturn repoSegment();\n\t\t\tcase 'context':\n\t\t\tcase 'environment':\n\t\t\t\treturn environment;\n\t\t\tcase 'repository':\n\t\t\t\treturn `repository:${claim(repo)}`;\n\t\t\tcase 'repository_owner':\n\t\t\t\treturn `repository_owner:${claim(owner)}`;\n\t\t\tcase 'repository_owner_id':\n\t\t\t\treturn `repository_owner_id:${facts!.ownerId}`;\n\t\t\tcase 'repository_id':\n\t\t\t\treturn `repository_id:${facts!.repoId}`;\n\t\t\tdefault:\n\t\t\t\treturn `repository_visibility:${claim(facts!.visibility)}`;\n\t\t}\n\t});\n\treturn { subject: parts.join(':'), kind: 'custom' };\n}\n\n/** What {@link resolveOidcSubject} found. */\nexport interface ResolvedOidcSubject {\n\tsubject: string;\n\tkind: OidcSubjectKind;\n\t/** Set when the format could not be read and the default was assumed. */\n\twarning?: string;\n}\n\n/** The endpoint a repository's subject format is read from. */\nexport function customizationEndpoint(repo: string): string {\n\treturn `repos/${repo}/actions/oidc/customization/sub`;\n}\n\n/**\n * The `sub` GitHub will send for `repo`'s `stage` environment, read from the\n * repository's OIDC settings through `gh`.\n *\n * A repository on a custom template that names no keys of its own uses its\n * organisation's, read from `orgs/{owner}/actions/oidc/customization/sub`.\n * When the settings cannot be read at all — no permission, or a GitHub\n * Enterprise Server without the endpoint — the default format is assumed, with\n * a warning saying where to check it.\n */\nexport function resolveOidcSubject(\n\tgh: Gh,\n\trepo: string,\n\tstage: string,\n): ResolvedOidcSubject {\n\tconst endpoint = customizationEndpoint(repo);\n\tlet customization: OidcSubjectCustomization;\n\ttry {\n\t\tcustomization = JSON.parse(gh(['api', endpoint]));\n\t} catch {\n\t\treturn {\n\t\t\tsubject: `repo:${repo}:environment:${claim(stage)}`,\n\t\t\tkind: 'assumed',\n\t\t\twarning: `Could not read ${endpoint}, so the default subject format is assumed. Check it with: gh api ${endpoint} — a repository on the immutable subject or a custom template sends a different sub, and the role would not be assumable.`,\n\t\t};\n\t}\n\n\tif (\n\t\t!customization.use_default &&\n\t\t(customization.include_claim_keys ?? []).length === 0\n\t) {\n\t\t// The repository follows its organisation's template.\n\t\tconst [owner = ''] = repo.split('/');\n\t\tconst orgEndpoint = `orgs/${owner}/actions/oidc/customization/sub`;\n\t\tlet org: { include_claim_keys?: string[]; use_immutable_subject?: boolean };\n\t\ttry {\n\t\t\torg = JSON.parse(gh(['api', orgEndpoint]));\n\t\t} catch {\n\t\t\tthrow new OrgOidcTemplateUnreadable(repo, orgEndpoint);\n\t\t}\n\t\tcustomization = {\n\t\t\t...customization,\n\t\t\tinclude_claim_keys: org.include_claim_keys ?? [],\n\t\t\tuse_immutable_subject:\n\t\t\t\tcustomization.use_immutable_subject ?? org.use_immutable_subject,\n\t\t};\n\t}\n\n\tconst facts = needsRepoFacts(customization)\n\t\t? readRepoFacts(gh, repo)\n\t\t: undefined;\n\treturn oidcSubject(repo, stage, customization, facts);\n}\n\nfunction readRepoFacts(gh: Gh, repo: string): RepoFacts {\n\tconst raw = JSON.parse(gh(['api', `repos/${repo}`])) as {\n\t\tid: number;\n\t\towner: { id: number };\n\t\tvisibility: string;\n\t};\n\treturn {\n\t\townerId: raw.owner.id,\n\t\trepoId: raw.id,\n\t\tvisibility: raw.visibility,\n\t};\n}\n\n/**\n * The repository's custom subject template includes a claim whose value\n * depends on the run — or names none — so no one subject can be trusted.\n */\nexport class OidcSubjectNotSupported extends GkmError {\n\tconstructor(\n\t\treadonly repo: string,\n\t\treadonly includeClaimKeys: string[],\n\t\treadonly unsupportedKeys: string[],\n\t) {\n\t\tsuper(\n\t\t\t`${repo} builds its OIDC subject from a custom template, ${JSON.stringify(includeClaimKeys)}, and ${\n\t\t\t\tunsupportedKeys.length > 0\n\t\t\t\t\t? `${unsupportedKeys.join(', ')} depend${unsupportedKeys.length === 1 ? 's' : ''} on the run`\n\t\t\t\t\t: 'it names no claims'\n\t\t\t}, so the deploy role cannot trust one exact subject. Set the repository back to the default subject — gh api --method PUT ${customizationEndpoint(repo)} -F use_default=true — (or keep the immutable one by also passing -F use_immutable_subject=true), or use a template built only from ${SUPPORTED_CLAIM_KEYS.join(', ')}; then run this again.`,\n\t\t);\n\t\tthis.name = 'OidcSubjectNotSupported';\n\t}\n}\n\n/** The subject needs the repository's ids, and none were read. */\nexport class RepoFactsRequired extends GkmError {\n\tconstructor(readonly repo: string) {\n\t\tsuper(\n\t\t\t`The OIDC subject of ${repo} includes its owner and repository ids; read them with gh api repos/${repo} and pass them in.`,\n\t\t);\n\t\tthis.name = 'RepoFactsRequired';\n\t}\n}\n\n/**\n * The repository uses its organisation's subject template, and that template\n * could not be read — so what GitHub sends is unknown, and is not guessed.\n */\nexport class OrgOidcTemplateUnreadable extends GkmError {\n\tconstructor(\n\t\treadonly repo: string,\n\t\treadonly endpoint: string,\n\t) {\n\t\tsuper(\n\t\t\t`${repo} uses its organisation's OIDC subject template, and ${endpoint} could not be read. Run this as an organisation owner (gh auth refresh -s admin:org), or set the repository back to the default subject with gh api --method PUT ${customizationEndpoint(repo)} -F use_default=true; then run this again.`,\n\t\t);\n\t\tthis.name = 'OrgOidcTemplateUnreadable';\n\t}\n}\n","/**\n * What the deploy role may do in the stage's account.\n *\n * An SST stage creates its own stack, which is most of AWS, so its role gets\n * `AdministratorAccess`. A stage deployed with the `compose` target builds and\n * runs containers on a server; from AWS its deploy job needs only the stage's\n * secrets — read, and written back when a deploy generates a new one — and,\n * when the deploy state is kept in AWS, the stage's state, and, when the\n * stage's domain is in Route53, the records of its hosted zone, and, when an\n * `s3` provider backs its buckets, those buckets and the IAM user each is\n * reached with — by the names the provider gives them. So a compose stage\n * gets an inline policy naming exactly those resources and nothing else.\n *\n * `--policy-arn` replaces either.\n */\n\nimport { stageDnsDomains } from '../compose/dns.js';\nimport { stageProvider } from '../providers/config.js';\nimport {\n\tPROJECT_BUCKET_CREATE_ACTIONS,\n\tprojectBucketName,\n} from '../providers/projectBucket.js';\nimport {\n\tbucketName,\n\tIAM_USER_PATH,\n\tiamUserName,\n\tSUFFIX_LENGTH,\n} from '../providers/s3/naming.js';\nimport { secretsParameterName } from '../secrets/aws.js';\nimport { secretsManagerSecretName } from '../secrets/secretsManager.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { deployIdentity } from './identity.js';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default\n * outside compose — said out loud in the output, and replaceable with\n * `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\n/** The name of the inline policy a compose stage's role is given. */\nexport const SCOPED_POLICY_NAME = 'gkm-deploy';\n\n/** One statement of an IAM policy document. */\nexport interface PolicyStatement {\n\tSid: string;\n\tEffect: 'Allow';\n\tAction: string[];\n\tResource: string | string[];\n\tCondition?: Record<string, Record<string, string>>;\n}\n\n/** What the role is given. */\nexport type DeployAccess =\n\t| {\n\t\t\tkind: 'managed';\n\t\t\tpolicyArn: string;\n\t\t\t/** Why, for the output. */\n\t\t\treason: string;\n\t }\n\t| {\n\t\t\tkind: 'scoped';\n\t\t\t/** The statements, in the account the role is in. */\n\t\t\tstatements(account: string): PolicyStatement[];\n\t\t\t/** What each statement is for, for the output. */\n\t\t\tdescribe: string[];\n\t };\n\n/** Every target the stage's apps deploy through. */\nexport function deployTargets(workspace: NormalizedWorkspace): string[] {\n\tconst targets = new Set<string>([workspace.deploy?.default ?? 'dokploy']);\n\tfor (const app of Object.values(workspace.apps)) {\n\t\ttargets.add(app.resolvedDeployTarget);\n\t}\n\treturn [...targets].sort();\n}\n\n/**\n * The access the stage's deploy role is given: `--policy-arn` when passed,\n * the scoped policy for a stage only the compose target deploys, and\n * `AdministratorAccess` otherwise.\n */\nexport function deployAccess(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\tpolicyArn?: string,\n\t/** The ids of the stage's `ObjectStorage` constructs, from its manifest. */\n\tbuckets: readonly string[] = [],\n): DeployAccess {\n\tif (policyArn) {\n\t\treturn { kind: 'managed', policyArn, reason: '--policy-arn' };\n\t}\n\n\tconst targets = deployTargets(workspace);\n\tif (targets.length !== 1 || targets[0] !== 'compose') {\n\t\treturn {\n\t\t\tkind: 'managed',\n\t\t\tpolicyArn: DEFAULT_POLICY_ARN,\n\t\t\treason: `the ${targets.join(', ')} target${targets.length === 1 ? '' : 's'} deploy${targets.length === 1 ? 's' : ''} infrastructure; --policy-arn to narrow`,\n\t\t};\n\t}\n\n\tconst project = workspace.name;\n\tconst store = workspace.secrets.store ?? 'file';\n\tconst state = workspace.state;\n\tconst custom =\n\t\t(typeof store === 'object' && typeof store.provider === 'object') ||\n\t\t(state !== undefined && typeof state.provider === 'object');\n\tif (custom) {\n\t\treturn {\n\t\t\tkind: 'managed',\n\t\t\tpolicyArn: DEFAULT_POLICY_ARN,\n\t\t\treason: `a custom ${typeof store === 'object' && typeof store.provider === 'object' ? 'secrets store' : 'state provider'} cannot be scoped; --policy-arn to narrow`,\n\t\t};\n\t}\n\n\tconst describe: string[] = [];\n\tconst build: ((account: string) => PolicyStatement[])[] = [];\n\n\tif (typeof store === 'object' && store.provider === 'ssm') {\n\t\tconst { region } = store;\n\t\tconst name = secretsParameterName(project, stage);\n\t\tdescribe.push(`read and update ${name} (SSM, ${region})`);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageSecrets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['ssm:GetParameter', 'ssm:PutParameter'],\n\t\t\t\tResource: `arn:aws:ssm:${region}:${account}:parameter${name}`,\n\t\t\t},\n\t\t]);\n\t} else if (\n\t\ttypeof store === 'object' &&\n\t\tstore.provider === 'secrets-manager'\n\t) {\n\t\tconst { region, kmsKeyId } = store as { region: string; kmsKeyId?: string };\n\t\tconst name = secretsManagerSecretName(project, stage);\n\t\tdescribe.push(`read and update ${name} (Secrets Manager, ${region})`);\n\t\tbuild.push((account) => {\n\t\t\tconst statements: PolicyStatement[] = [\n\t\t\t\t{\n\t\t\t\t\tSid: 'StageSecrets',\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tAction: [\n\t\t\t\t\t\t'secretsmanager:GetSecretValue',\n\t\t\t\t\t\t'secretsmanager:PutSecretValue',\n\t\t\t\t\t\t'secretsmanager:CreateSecret',\n\t\t\t\t\t],\n\t\t\t\t\t// Secrets Manager appends six random characters to a secret's ARN.\n\t\t\t\t\tResource: `arn:aws:secretsmanager:${region}:${account}:secret:${name}-??????`,\n\t\t\t\t},\n\t\t\t];\n\t\t\tif (kmsKeyId) {\n\t\t\t\tstatements.push({\n\t\t\t\t\tSid: 'StageSecretsKey',\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tAction: ['kms:Decrypt', 'kms:GenerateDataKey'],\n\t\t\t\t\tResource: kmsKeyId.startsWith('arn:') ? kmsKeyId : '*',\n\t\t\t\t\tCondition: {\n\t\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t\t'kms:ViaService': `secretsmanager.${region}.amazonaws.com`,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn statements;\n\t\t});\n\t}\n\n\tif (state?.provider === 'ssm') {\n\t\tconst { region } = state;\n\t\tconst path = `/gkm/${project}/${stage}/`;\n\t\tdescribe.push(\n\t\t\t`read and write the deploy state under ${path} (SSM, ${region})`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageState',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['ssm:GetParameter', 'ssm:PutParameter', 'ssm:DeleteParameter'],\n\t\t\t\tResource: `arn:aws:ssm:${region}:${account}:parameter${path}*`,\n\t\t\t},\n\t\t]);\n\t} else if (state?.provider === 's3') {\n\t\tconst prefix = (state.prefix ?? 'gkm').replace(/\\/+$/, '');\n\t\tconst path = `${prefix ? `${prefix}/` : ''}${project}/${stage}/`;\n\t\tconst named = state.bucket;\n\t\t// No bucket named: the project bucket, which the first deploy creates\n\t\t// — by a name the account fixes, so the role is given exactly it.\n\t\tconst bucketIn = (account: string) =>\n\t\t\tnamed ??\n\t\t\tprojectBucketName(deployIdentity(workspace, stage).scope, account);\n\t\tdescribe.push(\n\t\t\tnamed\n\t\t\t\t? `read and write the deploy state under s3://${named}/${path}`\n\t\t\t\t: `create the project bucket ${projectBucketName(deployIdentity(workspace, stage).scope, '<account>')}, and read and write the deploy state under ${path} in it`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t...stateBucketStatements(bucketIn(account), path),\n\t\t\t...(named\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSid: 'ProjectBucket',\n\t\t\t\t\t\t\tEffect: 'Allow' as const,\n\t\t\t\t\t\t\tAction: [...PROJECT_BUCKET_CREATE_ACTIONS],\n\t\t\t\t\t\t\tResource: `arn:aws:s3:::${bucketIn(account)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]),\n\t\t]);\n\t}\n\n\t// The deploy creates each bucket an s3 provider backs, and the IAM user\n\t// and key it is reached with: by their names — the good one, or the good\n\t// one with a clash's six-character suffix — and nothing else.\n\tconst objects = stageProvider(workspace, 'objects', stage);\n\tif (objects.mode === 'provider' && objects.name === 's3' && buckets.length) {\n\t\tconst { scope } = deployIdentity(workspace, stage);\n\t\tconst names = [...buckets].sort().map((id) => ({\n\t\t\tbucket: bucketName({ scope, stage, id }),\n\t\t\tuser: iamUserName({ scope, stage, id }),\n\t\t}));\n\t\tconst suffix = '?'.repeat(SUFFIX_LENGTH - 1);\n\t\tdescribe.push(\n\t\t\t`create and configure the buckets ${names.map((n) => n.bucket).join(', ')} and their IAM users (S3, IAM)`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageBuckets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:CreateBucket',\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t's3:GetBucketTagging',\n\t\t\t\t\t's3:PutBucketTagging',\n\t\t\t\t\t's3:GetBucketPublicAccessBlock',\n\t\t\t\t\t's3:PutBucketPublicAccessBlock',\n\t\t\t\t\t's3:GetBucketPolicy',\n\t\t\t\t\t's3:PutBucketPolicy',\n\t\t\t\t\t's3:GetEncryptionConfiguration',\n\t\t\t\t\t's3:PutEncryptionConfiguration',\n\t\t\t\t\t's3:GetBucketVersioning',\n\t\t\t\t\t's3:PutBucketVersioning',\n\t\t\t\t\t's3:GetBucketCORS',\n\t\t\t\t\t's3:PutBucketCORS',\n\t\t\t\t],\n\t\t\t\tResource: names.flatMap((n) => [\n\t\t\t\t\t`arn:aws:s3:::${n.bucket}`,\n\t\t\t\t\t`arn:aws:s3:::${n.bucket}-${suffix}`,\n\t\t\t\t]),\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageBucketUsers',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'iam:GetUser',\n\t\t\t\t\t'iam:CreateUser',\n\t\t\t\t\t'iam:TagUser',\n\t\t\t\t\t'iam:ListUserTags',\n\t\t\t\t\t'iam:GetUserPolicy',\n\t\t\t\t\t'iam:PutUserPolicy',\n\t\t\t\t\t'iam:ListAccessKeys',\n\t\t\t\t\t'iam:CreateAccessKey',\n\t\t\t\t\t'iam:DeleteAccessKey',\n\t\t\t\t],\n\t\t\t\tResource: names.map(\n\t\t\t\t\t(n) => `arn:aws:iam::${account}:user${IAM_USER_PATH}${n.user}`,\n\t\t\t\t),\n\t\t\t},\n\t\t]);\n\t}\n\n\t// The deploy writes the stage's hosts' records: in Route53, only that\n\t// zone's — and, with no hostedZoneId to name it, the zone is looked up\n\t// by name, which IAM cannot scope.\n\tfor (const { domain, config } of stageDnsDomains(\n\t\tstage,\n\t\tworkspace.domains,\n\t\tworkspace.dns,\n\t)) {\n\t\tif (config.provider !== 'route53') continue;\n\t\tconst zoneId = (config as { hostedZoneId?: string }).hostedZoneId?.replace(\n\t\t\t/^\\/?hostedzone\\//,\n\t\t\t'',\n\t\t);\n\t\tdescribe.push(\n\t\t\t`write the A, AAAA and CNAME records of ${domain} (Route53${zoneId ? `, ${zoneId}` : ''})`,\n\t\t);\n\t\tbuild.push(() => [\n\t\t\t{\n\t\t\t\tSid: 'StageDnsRecords',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'route53:ChangeResourceRecordSets',\n\t\t\t\t\t'route53:ListResourceRecordSets',\n\t\t\t\t],\n\t\t\t\tResource: `arn:aws:route53:::hostedzone/${zoneId ?? '*'}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageDnsChanges',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['route53:GetChange'],\n\t\t\t\tResource: 'arn:aws:route53:::change/*',\n\t\t\t},\n\t\t\t...(zoneId\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSid: 'StageDnsZones',\n\t\t\t\t\t\t\tEffect: 'Allow' as const,\n\t\t\t\t\t\t\tAction: ['route53:ListHostedZonesByName'],\n\t\t\t\t\t\t\tResource: '*',\n\t\t\t\t\t\t},\n\t\t\t\t\t]),\n\t\t]);\n\t}\n\n\treturn {\n\t\tkind: 'scoped',\n\t\tstatements: (account) => build.flatMap((statements) => statements(account)),\n\t\tdescribe,\n\t};\n}\n\n/**\n * Reading and writing one stage's deploy state in `bucket`, as the deploy\n * role is given it.\n */\nexport function stateBucketStatements(\n\tbucket: string,\n\tpath: string,\n): PolicyStatement[] {\n\treturn [\n\t\t{\n\t\t\tSid: 'StageState',\n\t\t\tEffect: 'Allow',\n\t\t\tAction: ['s3:GetObject', 's3:PutObject', 's3:DeleteObject'],\n\t\t\tResource: `arn:aws:s3:::${bucket}/${path}*`,\n\t\t},\n\t\t// HeadBucket, and a state not written yet a 404 rather than a 403.\n\t\t{\n\t\t\tSid: 'StageStateBucket',\n\t\t\tEffect: 'Allow',\n\t\t\tAction: ['s3:ListBucket'],\n\t\t\tResource: `arn:aws:s3:::${bucket}`,\n\t\t},\n\t];\n}\n\n/** The scoped access as a policy document, or null when it allows nothing. */\nexport function scopedPolicyDocument(\n\tstatements: PolicyStatement[],\n): string | null {\n\tif (statements.length === 0) return null;\n\treturn JSON.stringify({ Version: '2012-10-17', Statement: statements });\n}\n\n/** The account an IAM ARN is in: `arn:aws:iam::<account>:…`. */\nexport function accountOf(arn: string): string {\n\treturn arn.split(':')[4] ?? '';\n}\n","/**\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 or Secrets Manager the deploy job reads them with the role instead, so\n * no key is handed to GitHub, and this checks the stage's store in the same\n * account, with the same profile. Re-running it converges rather than duplicating.\n *\n * The role trusts the exact `sub` GitHub sends for the environment, read from\n * the repository's OIDC subject settings (`githubOidc.ts`) — the default, the\n * immutable subject with the owner's and repository's ids, or a custom\n * template. A re-run rewrites an existing role's trust to it, so running this\n * again repairs a role that trusted the wrong format. What the role may do is\n * `githubPolicy.ts`: `AdministratorAccess` for SST, and for a compose stage\n * only its own secrets (and deploy state, when that is in AWS).\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tDeleteRolePolicyCommand,\n\tDetachRolePolicyCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListAttachedRolePoliciesCommand,\n\tListOpenIDConnectProvidersCommand,\n\tListRoleTagsCommand,\n\tPutRolePolicyCommand,\n\tTagRoleCommand,\n\tUntagRoleCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { GkmError } from '../errors';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { getKeyPath, keystoreProject, readKey } from '../secrets/keystore.js';\nimport { isRemoteStore, secretsStoreFor } from '../secrets/store.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport { resolveOidcSubject } from './githubOidc.js';\nimport {\n\taccountOf,\n\tdeployAccess,\n\tSCOPED_POLICY_NAME,\n\tscopedPolicyDocument,\n} from './githubPolicy.js';\n\nexport { DEFAULT_POLICY_ARN, SCOPED_POLICY_NAME } from './githubPolicy.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * The tag recording which managed policy gkm attached to the role — so a\n * re-run detaches only what gkm put there, never a policy someone else did.\n */\nexport const POLICY_TAG = 'gkm:policy-arn';\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, in place of the default. */\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// stderr captured, so a read that fails (a 404) is not printed as noise.\n\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t...(input === undefined ? {} : { input }),\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 whose OIDC token carries exactly\n * `subject` — the repository's `<stage>` environment, in the format the\n * repository sends (see `githubOidc.ts`), and nothing else. Not another\n * branch's job, not another environment's: that is what keeps a staging\n * deploy from using the production role.\n */\nexport function trustPolicy(providerArn: string, subject: string): 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`]: subject,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/**\n * The subject a trust policy trusts, or undefined when it names none. IAM\n * hands documents back URL-encoded.\n */\nexport function trustedSubject(\n\tdocument: string | undefined,\n): string | undefined {\n\tif (!document) return undefined;\n\tlet parsed: {\n\t\tStatement?: { Condition?: Record<string, Record<string, unknown>> }[];\n\t};\n\ttry {\n\t\tparsed = JSON.parse(safeDecode(document));\n\t} catch {\n\t\treturn undefined;\n\t}\n\tfor (const statement of parsed.Statement ?? []) {\n\t\tfor (const operator of Object.values(statement.Condition ?? {})) {\n\t\t\tconst value = operator[`${GITHUB_OIDC_HOST}:sub`];\n\t\t\tif (typeof value === 'string') return value;\n\t\t\tif (Array.isArray(value)) return value.join(', ');\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction safeDecode(value: string): string {\n\ttry {\n\t\treturn decodeURIComponent(value);\n\t} catch {\n\t\treturn value;\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/**\n * What the role is given: a managed policy, attached; or an inline policy\n * document — null for none at all.\n */\nexport type RoleAccess = { managed: string } | { inline: string | null };\n\n/** What {@link ensureRole} did. */\nexport interface RoleOutcome {\n\tarn: string;\n\tcreated: boolean;\n\t/** The subject the role trusted before, when it existed. */\n\tpreviousSubject?: string;\n\t/** Managed policies gkm had attached and detached now. */\n\tdetached: string[];\n\t/**\n\t * Managed policies still attached that the role no longer needs, which gkm\n\t * did not record attaching — left alone, for a person to detach.\n\t */\n\tleftAttached: string[];\n}\n\nfunction isNoSuchEntity(error: unknown): boolean {\n\treturn (error as { name?: string }).name === 'NoSuchEntityException';\n}\n\n/**\n * The deploy role, created or brought back to this trust and access.\n *\n * Converges: re-running rewrites the trust, and moves the role from one access\n * to another — detaching a managed policy only when the role's\n * {@link POLICY_TAG} says gkm attached it.\n */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\taccess: RoleAccess,\n): Promise<RoleOutcome> {\n\tlet arn: string;\n\tlet created = false;\n\tlet previousSubject: string | undefined;\n\tlet recorded: string | undefined;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tpreviousSubject = trustedSubject(Role!.AssumeRolePolicyDocument);\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\tconst { Tags = [] } = await iam.send(\n\t\t\tnew ListRoleTagsCommand({ RoleName: name }),\n\t\t);\n\t\trecorded = Tags.find((tag) => tag.Key === POLICY_TAG)?.Value;\n\t} catch (error) {\n\t\tif (!isNoSuchEntity(error)) throw error;\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\tcreated = true;\n\t}\n\n\tconst detached: string[] = [];\n\tconst leftAttached: string[] = [];\n\tconst detach = async (policyArn: string) => {\n\t\ttry {\n\t\t\tawait iam.send(\n\t\t\t\tnew DetachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t\t\t);\n\t\t\tdetached.push(policyArn);\n\t\t} catch (error) {\n\t\t\t// Already gone — by hand, or a run that stopped halfway.\n\t\t\tif (!isNoSuchEntity(error)) throw error;\n\t\t}\n\t};\n\tconst deleteInline = async () => {\n\t\ttry {\n\t\t\tawait iam.send(\n\t\t\t\tnew DeleteRolePolicyCommand({\n\t\t\t\t\tRoleName: name,\n\t\t\t\t\tPolicyName: SCOPED_POLICY_NAME,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!isNoSuchEntity(error)) throw error;\n\t\t}\n\t};\n\n\tif ('managed' in access) {\n\t\t// Attaching an attached policy is a no-op, so this converges too.\n\t\tawait iam.send(\n\t\t\tnew AttachRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyArn: access.managed,\n\t\t\t}),\n\t\t);\n\t\tif (recorded && recorded !== access.managed) await detach(recorded);\n\t\tif (recorded !== access.managed) {\n\t\t\tawait iam.send(\n\t\t\t\tnew TagRoleCommand({\n\t\t\t\t\tRoleName: name,\n\t\t\t\t\tTags: [{ Key: POLICY_TAG, Value: access.managed }],\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (!created) await deleteInline();\n\t\treturn { arn, created, previousSubject, detached, leftAttached };\n\t}\n\n\tif (access.inline) {\n\t\tawait iam.send(\n\t\t\tnew PutRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyName: SCOPED_POLICY_NAME,\n\t\t\t\tPolicyDocument: access.inline,\n\t\t\t}),\n\t\t);\n\t} else if (!created) {\n\t\tawait deleteInline();\n\t}\n\tif (created) return { arn, created, previousSubject, detached, leftAttached };\n\n\tif (recorded) {\n\t\tawait detach(recorded);\n\t\tawait iam.send(\n\t\t\tnew UntagRoleCommand({ RoleName: name, TagKeys: [POLICY_TAG] }),\n\t\t);\n\t}\n\t// A role from before gkm recorded what it attached: what is attached may\n\t// be gkm's or someone's own, so it is named, not detached.\n\tconst { AttachedPolicies = [] } = await iam.send(\n\t\tnew ListAttachedRolePoliciesCommand({ RoleName: name }),\n\t);\n\tfor (const policy of AttachedPolicies) {\n\t\tif (policy.PolicyArn && !detached.includes(policy.PolicyArn)) {\n\t\t\tleftAttached.push(policy.PolicyArn);\n\t\t}\n\t}\n\treturn { arn, created, previousSubject, detached, leftAttached };\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; subject: 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\t// The exact subject GitHub will send — read from the repository's OIDC\n\t// settings, before anything is changed, so a format no role can trust\n\t// fails here.\n\tconst oidc = resolveOidcSubject(gh, repo, options.stage);\n\t// The buckets an s3 provider creates are named after their constructs,\n\t// so the role is scoped to exactly those names.\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t});\n\tconst buckets = Object.entries(manifest)\n\t\t.filter(([, d]) => d.kind === 'objects')\n\t\t.map(([id]) => id);\n\tconst access = deployAccess(\n\t\tworkspace,\n\t\toptions.stage,\n\t\toptions.policyArn,\n\t\tbuckets,\n\t);\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\tif (access.kind === 'managed') {\n\t\tlogger.log(` Policy: ${access.policyArn} (${access.reason})`);\n\t} else if (access.describe.length > 0) {\n\t\tlogger.log(\n\t\t\t` Policy: inline \"${SCOPED_POLICY_NAME}\", scoped to the compose deploy — only:`,\n\t\t);\n\t\tfor (const line of access.describe) logger.log(` ${line}`);\n\t} else {\n\t\tlogger.log(\n\t\t\t' Policy: none — a compose deploy with file secrets and local state reads nothing from AWS',\n\t\t);\n\t}\n\tlogger.log(\n\t\t` Trusted by: ${oidc.subject} only (${SUBJECT_KINDS[oidc.kind]})`,\n\t);\n\tif (oidc.warning) logger.log(` ⚠ ${oidc.warning}`);\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\tif (access.kind === 'scoped') {\n\t\t\tconst document = scopedPolicyDocument(access.statements('<account>'));\n\t\t\tif (document) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`\\n Inline policy:\\n${JSON.stringify(JSON.parse(document), null, 2).replace(/^/gm, ' ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo, subject: oidc.subject };\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 outcome = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, oidc.subject),\n\t\taccess.kind === 'managed'\n\t\t\t? { managed: access.policyArn }\n\t\t\t: {\n\t\t\t\t\tinline: scopedPolicyDocument(\n\t\t\t\t\t\taccess.statements(accountOf(providerArn)),\n\t\t\t\t\t),\n\t\t\t\t},\n\t);\n\tconst roleArn = outcome.arn;\n\tlogger.log(`\\n ✓ AWS: ${roleArn}${outcome.created ? ' (created)' : ''}`);\n\tif (!outcome.created) {\n\t\tlogger.log(\n\t\t\toutcome.previousSubject === oidc.subject\n\t\t\t\t? ` ✓ Trust: unchanged, ${oidc.subject}`\n\t\t\t\t: ` ✓ Trust: ${outcome.previousSubject ?? '(no subject)'} → ${oidc.subject}`,\n\t\t);\n\t}\n\tfor (const detached of outcome.detached) {\n\t\tlogger.log(` ✓ Detached ${detached} (attached by gkm, no longer needed)`);\n\t}\n\tif (access.kind === 'scoped') {\n\t\tfor (const attached of outcome.leftAttached) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ ${attached} is still attached to ${role}. gkm has no record of attaching it, so it is left; the compose deploy does not need it. Detach it with: aws iam detach-role-policy --role-name ${role} --policy-arn ${attached}${options.profile ? ` --profile ${options.profile}` : ''}`,\n\t\t\t);\n\t\t}\n\t}\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, subject: oidc.subject };\n}\n\nconst SUBJECT_KINDS = {\n\tdefault: \"the repository's default subject\",\n\timmutable: \"the repository's immutable subject\",\n\tcustom: \"the repository's custom subject template\",\n\tassumed: 'assumed: the default format',\n} as const;\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends GkmError {\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,94 +0,0 @@
1
- import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
2
- import { N as assertDeployedStage, dt as SECRETS_STORE_PROVIDERS, ft as UnknownSecretsStoreProvider } from "./workspace-Bv4xROeu.mjs";
3
- import { loadWorkspaceSettings } from "./config.mjs";
4
- import { On as secretsStoreFor, kn as storeFromConfig } from "./seeds-BsJRUL1K.mjs";
5
- //#region src/secrets/migrate.ts
6
- /**
7
- * `gkm secrets:migrate` — copy a deployed stage's secrets from the store
8
- * `secrets.store` names to another one, whole: service passwords, URLs and
9
- * custom keys alike, so nothing is regenerated and running services keep
10
- * their credentials.
11
- *
12
- * It copies and nothing more. The source is left as it was, and gkm.config.ts
13
- * is not edited: pointing `secrets.store` at the new store is the switch, and
14
- * the old copy is deleted by hand once deploys read from the new one.
15
- */
16
- const logger = console;
17
- /** An AWS store to copy to, with no region given and none configured. */
18
- var MigrateTargetNeedsRegion = class extends GkmError {
19
- to;
20
- constructor(to) {
21
- super(`Copying to ${to} needs a region, and secrets.store has none to reuse. Pass it: gkm secrets:migrate --to ${to} --region <region> --stage <stage>.`);
22
- this.to = to;
23
- this.name = "MigrateTargetNeedsRegion";
24
- }
25
- };
26
- /** The target is the store the stage is already read from. */
27
- var MigrateTargetIsSource = class extends GkmError {
28
- stage;
29
- to;
30
- constructor(stage, to) {
31
- super(`Stage "${stage}" is already kept in ${to}, so there is nothing to copy. Name a different store with --to, or a different region with --region.`);
32
- this.stage = stage;
33
- this.to = to;
34
- this.name = "MigrateTargetIsSource";
35
- }
36
- };
37
- /** The configured store holds nothing for the stage. */
38
- var NoSecretsToMigrate = class extends GkmError {
39
- stage;
40
- store;
41
- constructor(stage, store) {
42
- super(`The ${store} store holds no secrets for stage "${stage}", so there is nothing to copy. Check secrets.store in gkm.config.ts still names the store the stage is in, or create the stage there with gkm secrets:init --stage ${stage}.`);
43
- this.stage = stage;
44
- this.store = store;
45
- this.name = "NoSecretsToMigrate";
46
- }
47
- };
48
- /** The target already holds the stage, and `--force` was not given. */
49
- var MigrateTargetHoldsStage = class extends GkmError {
50
- stage;
51
- to;
52
- constructor(stage, to) {
53
- super(`The ${to} store already holds secrets for stage "${stage}". Pass --force to replace them with the ones being copied.`);
54
- this.stage = stage;
55
- this.to = to;
56
- this.name = "MigrateTargetHoldsStage";
57
- }
58
- };
59
- /** The store a `--to` name and region describe. */
60
- function targetConfig(to, region, configured) {
61
- if (!SECRETS_STORE_PROVIDERS.includes(to)) throw new UnknownSecretsStoreProvider(to);
62
- const provider = to;
63
- if (provider === "file") return "file";
64
- const fallback = typeof configured === "object" && "region" in configured ? configured.region : void 0;
65
- const resolved = region ?? fallback;
66
- if (!resolved) throw new MigrateTargetNeedsRegion(provider);
67
- return {
68
- provider,
69
- region: resolved
70
- };
71
- }
72
- async function secretsMigrateCommand(options) {
73
- const { stage, to } = options;
74
- const workspace = await loadWorkspaceSettings();
75
- assertDeployedStage(workspace.stages, stage);
76
- const configured = workspace.secrets.store ?? "file";
77
- const target = targetConfig(to, options.region, configured);
78
- if (typeof configured === "string" || typeof target === "string" ? configured === target : configured.provider === target.provider && "region" in configured && "region" in target && configured.region === target.region) throw new MigrateTargetIsSource(stage, to);
79
- const storeOptions = options.profile ? { profile: options.profile } : {};
80
- const source = await secretsStoreFor(workspace, stage, storeOptions);
81
- const destination = await storeFromConfig(workspace, target, storeOptions);
82
- const secrets = await source.read(stage);
83
- if (!secrets) throw new NoSecretsToMigrate(stage, source.name);
84
- if (!options.force && await destination.read(stage)) throw new MigrateTargetHoldsStage(stage, destination.name);
85
- await destination.write(stage, secrets);
86
- const shape = typeof target === "object" && "region" in target ? `{ provider: '${target.provider}', region: '${target.region}' }` : `'file'`;
87
- logger.log(`\n✓ Copied the secrets for stage "${stage}" from ${source.name} to ${destination.name}`);
88
- logger.log(`\n Next: set secrets.store to ${shape} in gkm.config.ts, so commands and deploys read from ${destination.name}.`);
89
- logger.log(` The copy in ${source.name} is left as it was; delete it once deploys read from ${destination.name}.`);
90
- }
91
- //#endregion
92
- export { secretsMigrateCommand };
93
-
94
- //# sourceMappingURL=migrate-C230QeXU.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrate-C230QeXU.mjs","names":[],"sources":["../src/secrets/migrate.ts"],"sourcesContent":["/**\n * `gkm secrets:migrate` — copy a deployed stage's secrets from the store\n * `secrets.store` names to another one, whole: service passwords, URLs and\n * custom keys alike, so nothing is regenerated and running services keep\n * their credentials.\n *\n * It copies and nothing more. The source is left as it was, and gkm.config.ts\n * is not edited: pointing `secrets.store` at the new store is the switch, and\n * the old copy is deleted by hand once deploys read from the new one.\n */\n\nimport { loadWorkspaceSettings } from '../config.js';\nimport { GkmError } from '../errors';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport {\n\tSECRETS_STORE_PROVIDERS,\n\ttype SecretsStoreConfig,\n\ttype SecretsStoreOptions,\n\ttype SecretsStoreProvider,\n\tsecretsStoreFor,\n\tstoreFromConfig,\n\tUnknownSecretsStoreProvider,\n} from './store.js';\n\nconst logger = console;\n\nexport interface SecretsMigrateOptions {\n\tstage: string;\n\t/** The store to copy to: `'file'`, `'ssm'` or `'secrets-manager'`. */\n\tto: string;\n\t/** The target's AWS region; defaults to the configured store's. */\n\tregion?: string;\n\t/** Overwrite a stage the target already holds. */\n\tforce?: boolean;\n\t/** The AWS profile for the stage's account, for both stores. */\n\tprofile?: string;\n}\n\n/** An AWS store to copy to, with no region given and none configured. */\nexport class MigrateTargetNeedsRegion extends GkmError {\n\tconstructor(readonly to: string) {\n\t\tsuper(\n\t\t\t`Copying to ${to} needs a region, and secrets.store has none to reuse. ` +\n\t\t\t\t`Pass it: gkm secrets:migrate --to ${to} --region <region> --stage <stage>.`,\n\t\t);\n\t\tthis.name = 'MigrateTargetNeedsRegion';\n\t}\n}\n\n/** The target is the store the stage is already read from. */\nexport class MigrateTargetIsSource extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly to: string,\n\t) {\n\t\tsuper(\n\t\t\t`Stage \"${stage}\" is already kept in ${to}, so there is nothing to copy. ` +\n\t\t\t\t'Name a different store with --to, or a different region with --region.',\n\t\t);\n\t\tthis.name = 'MigrateTargetIsSource';\n\t}\n}\n\n/** The configured store holds nothing for the stage. */\nexport class NoSecretsToMigrate extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly store: string,\n\t) {\n\t\tsuper(\n\t\t\t`The ${store} store holds no secrets for stage \"${stage}\", so there is nothing to copy. ` +\n\t\t\t\t`Check secrets.store in gkm.config.ts still names the store the stage is in, ` +\n\t\t\t\t`or create the stage there with gkm secrets:init --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoSecretsToMigrate';\n\t}\n}\n\n/** The target already holds the stage, and `--force` was not given. */\nexport class MigrateTargetHoldsStage extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly to: string,\n\t) {\n\t\tsuper(\n\t\t\t`The ${to} store already holds secrets for stage \"${stage}\". ` +\n\t\t\t\t'Pass --force to replace them with the ones being copied.',\n\t\t);\n\t\tthis.name = 'MigrateTargetHoldsStage';\n\t}\n}\n\n/** The store a `--to` name and region describe. */\nfunction targetConfig(\n\tto: string,\n\tregion: string | undefined,\n\tconfigured: SecretsStoreConfig,\n): SecretsStoreConfig {\n\tif (!(SECRETS_STORE_PROVIDERS as readonly string[]).includes(to)) {\n\t\tthrow new UnknownSecretsStoreProvider(to);\n\t}\n\tconst provider = to as SecretsStoreProvider;\n\tif (provider === 'file') return 'file';\n\n\tconst fallback =\n\t\ttypeof configured === 'object' && 'region' in configured\n\t\t\t? configured.region\n\t\t\t: undefined;\n\tconst resolved = region ?? fallback;\n\tif (!resolved) throw new MigrateTargetNeedsRegion(provider);\n\treturn { provider, region: resolved };\n}\n\nexport async function secretsMigrateCommand(\n\toptions: SecretsMigrateOptions,\n): Promise<void> {\n\tconst { stage, to } = options;\n\tconst workspace = await loadWorkspaceSettings();\n\tassertDeployedStage(workspace.stages, stage);\n\n\tconst configured = workspace.secrets.store ?? 'file';\n\tconst target = targetConfig(to, options.region, configured);\n\tconst sameStore =\n\t\ttypeof configured === 'string' || typeof target === 'string'\n\t\t\t? configured === target\n\t\t\t: configured.provider === target.provider &&\n\t\t\t\t'region' in configured &&\n\t\t\t\t'region' in target &&\n\t\t\t\tconfigured.region === target.region;\n\tif (sameStore) throw new MigrateTargetIsSource(stage, to);\n\n\tconst storeOptions: SecretsStoreOptions = options.profile\n\t\t? { profile: options.profile }\n\t\t: {};\n\tconst source = await secretsStoreFor(workspace, stage, storeOptions);\n\tconst destination = await storeFromConfig(workspace, target, storeOptions);\n\n\tconst secrets = await source.read(stage);\n\tif (!secrets) throw new NoSecretsToMigrate(stage, source.name);\n\n\tif (!options.force && (await destination.read(stage))) {\n\t\tthrow new MigrateTargetHoldsStage(stage, destination.name);\n\t}\n\n\tawait destination.write(stage, secrets);\n\n\tconst shape =\n\t\ttypeof target === 'object' && 'region' in target\n\t\t\t? `{ provider: '${target.provider}', region: '${target.region}' }`\n\t\t\t: `'file'`;\n\tlogger.log(\n\t\t`\\n✓ Copied the secrets for stage \"${stage}\" from ${source.name} to ${destination.name}`,\n\t);\n\tlogger.log(\n\t\t`\\n Next: set secrets.store to ${shape} in gkm.config.ts, so commands and deploys read from ${destination.name}.`,\n\t);\n\tlogger.log(\n\t\t` The copy in ${source.name} is left as it was; delete it once deploys read from ${destination.name}.`,\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAwBA,MAAM,SAAS;;AAef,IAAa,2BAAb,cAA8C,SAAS;CACjC;CAArB,YAAY,IAAqB;EAChC,MACC,cAAc,GAAG,0FACqB,GAAG,oCAC1C;EAJoB,KAAA,KAAA;EAKpB,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,wBAAb,cAA2C,SAAS;CAEzC;CACA;CAFV,YACC,OACA,IACC;EACD,MACC,UAAU,MAAM,uBAAuB,GAAG,sGAE3C;EANS,KAAA,QAAA;EACA,KAAA,KAAA;EAMT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,qBAAb,cAAwC,SAAS;CAEtC;CACA;CAFV,YACC,OACA,OACC;EACD,MACC,OAAO,MAAM,qCAAqC,MAAM,sKAEI,MAAM,EACnE;EAPS,KAAA,QAAA;EACA,KAAA,QAAA;EAOT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,0BAAb,cAA6C,SAAS;CAE3C;CACA;CAFV,YACC,OACA,IACC;EACD,MACC,OAAO,GAAG,0CAA0C,MAAM,4DAE3D;EANS,KAAA,QAAA;EACA,KAAA,KAAA;EAMT,KAAK,OAAO;CACb;AACD;;AAGA,SAAS,aACR,IACA,QACA,YACqB;CACrB,IAAI,CAAE,wBAA8C,SAAS,EAAE,GAC9D,MAAM,IAAI,4BAA4B,EAAE;CAEzC,MAAM,WAAW;CACjB,IAAI,aAAa,QAAQ,OAAO;CAEhC,MAAM,WACL,OAAO,eAAe,YAAY,YAAY,aAC3C,WAAW,SACX,KAAA;CACJ,MAAM,WAAW,UAAU;CAC3B,IAAI,CAAC,UAAU,MAAM,IAAI,yBAAyB,QAAQ;CAC1D,OAAO;EAAE;EAAU,QAAQ;CAAS;AACrC;AAEA,eAAsB,sBACrB,SACgB;CAChB,MAAM,EAAE,OAAO,OAAO;CACtB,MAAM,YAAY,MAAM,sBAAsB;CAC9C,oBAAoB,UAAU,QAAQ,KAAK;CAE3C,MAAM,aAAa,UAAU,QAAQ,SAAS;CAC9C,MAAM,SAAS,aAAa,IAAI,QAAQ,QAAQ,UAAU;CAQ1D,IANC,OAAO,eAAe,YAAY,OAAO,WAAW,WACjD,eAAe,SACf,WAAW,aAAa,OAAO,YAChC,YAAY,cACZ,YAAY,UACZ,WAAW,WAAW,OAAO,QACjB,MAAM,IAAI,sBAAsB,OAAO,EAAE;CAExD,MAAM,eAAoC,QAAQ,UAC/C,EAAE,SAAS,QAAQ,QAAQ,IAC3B,CAAC;CACJ,MAAM,SAAS,MAAM,gBAAgB,WAAW,OAAO,YAAY;CACnE,MAAM,cAAc,MAAM,gBAAgB,WAAW,QAAQ,YAAY;CAEzE,MAAM,UAAU,MAAM,OAAO,KAAK,KAAK;CACvC,IAAI,CAAC,SAAS,MAAM,IAAI,mBAAmB,OAAO,OAAO,IAAI;CAE7D,IAAI,CAAC,QAAQ,SAAU,MAAM,YAAY,KAAK,KAAK,GAClD,MAAM,IAAI,wBAAwB,OAAO,YAAY,IAAI;CAG1D,MAAM,YAAY,MAAM,OAAO,OAAO;CAEtC,MAAM,QACL,OAAO,WAAW,YAAY,YAAY,SACvC,gBAAgB,OAAO,SAAS,cAAc,OAAO,OAAO,OAC5D;CACJ,OAAO,IACN,qCAAqC,MAAM,SAAS,OAAO,KAAK,MAAM,YAAY,MACnF;CACA,OAAO,IACN,kCAAkC,MAAM,uDAAuD,YAAY,KAAK,EACjH;CACA,OAAO,IACN,iBAAiB,OAAO,KAAK,uDAAuD,YAAY,KAAK,EACtG;AACD"}
@@ -1,94 +0,0 @@
1
- const require_errors = require("./errors-DMC4Umsb.cjs");
2
- const require_workspace = require("./workspace-C1M-S4al.cjs");
3
- const require_config = require("./config.cjs");
4
- const require_seeds = require("./seeds-BQlj9Fsg.cjs");
5
- //#region src/secrets/migrate.ts
6
- /**
7
- * `gkm secrets:migrate` — copy a deployed stage's secrets from the store
8
- * `secrets.store` names to another one, whole: service passwords, URLs and
9
- * custom keys alike, so nothing is regenerated and running services keep
10
- * their credentials.
11
- *
12
- * It copies and nothing more. The source is left as it was, and gkm.config.ts
13
- * is not edited: pointing `secrets.store` at the new store is the switch, and
14
- * the old copy is deleted by hand once deploys read from the new one.
15
- */
16
- const logger = console;
17
- /** An AWS store to copy to, with no region given and none configured. */
18
- var MigrateTargetNeedsRegion = class extends require_errors.GkmError {
19
- to;
20
- constructor(to) {
21
- super(`Copying to ${to} needs a region, and secrets.store has none to reuse. Pass it: gkm secrets:migrate --to ${to} --region <region> --stage <stage>.`);
22
- this.to = to;
23
- this.name = "MigrateTargetNeedsRegion";
24
- }
25
- };
26
- /** The target is the store the stage is already read from. */
27
- var MigrateTargetIsSource = class extends require_errors.GkmError {
28
- stage;
29
- to;
30
- constructor(stage, to) {
31
- super(`Stage "${stage}" is already kept in ${to}, so there is nothing to copy. Name a different store with --to, or a different region with --region.`);
32
- this.stage = stage;
33
- this.to = to;
34
- this.name = "MigrateTargetIsSource";
35
- }
36
- };
37
- /** The configured store holds nothing for the stage. */
38
- var NoSecretsToMigrate = class extends require_errors.GkmError {
39
- stage;
40
- store;
41
- constructor(stage, store) {
42
- super(`The ${store} store holds no secrets for stage "${stage}", so there is nothing to copy. Check secrets.store in gkm.config.ts still names the store the stage is in, or create the stage there with gkm secrets:init --stage ${stage}.`);
43
- this.stage = stage;
44
- this.store = store;
45
- this.name = "NoSecretsToMigrate";
46
- }
47
- };
48
- /** The target already holds the stage, and `--force` was not given. */
49
- var MigrateTargetHoldsStage = class extends require_errors.GkmError {
50
- stage;
51
- to;
52
- constructor(stage, to) {
53
- super(`The ${to} store already holds secrets for stage "${stage}". Pass --force to replace them with the ones being copied.`);
54
- this.stage = stage;
55
- this.to = to;
56
- this.name = "MigrateTargetHoldsStage";
57
- }
58
- };
59
- /** The store a `--to` name and region describe. */
60
- function targetConfig(to, region, configured) {
61
- if (!require_workspace.SECRETS_STORE_PROVIDERS.includes(to)) throw new require_workspace.UnknownSecretsStoreProvider(to);
62
- const provider = to;
63
- if (provider === "file") return "file";
64
- const fallback = typeof configured === "object" && "region" in configured ? configured.region : void 0;
65
- const resolved = region ?? fallback;
66
- if (!resolved) throw new MigrateTargetNeedsRegion(provider);
67
- return {
68
- provider,
69
- region: resolved
70
- };
71
- }
72
- async function secretsMigrateCommand(options) {
73
- const { stage, to } = options;
74
- const workspace = await require_config.loadWorkspaceSettings();
75
- require_workspace.assertDeployedStage(workspace.stages, stage);
76
- const configured = workspace.secrets.store ?? "file";
77
- const target = targetConfig(to, options.region, configured);
78
- if (typeof configured === "string" || typeof target === "string" ? configured === target : configured.provider === target.provider && "region" in configured && "region" in target && configured.region === target.region) throw new MigrateTargetIsSource(stage, to);
79
- const storeOptions = options.profile ? { profile: options.profile } : {};
80
- const source = await require_seeds.secretsStoreFor(workspace, stage, storeOptions);
81
- const destination = await require_seeds.storeFromConfig(workspace, target, storeOptions);
82
- const secrets = await source.read(stage);
83
- if (!secrets) throw new NoSecretsToMigrate(stage, source.name);
84
- if (!options.force && await destination.read(stage)) throw new MigrateTargetHoldsStage(stage, destination.name);
85
- await destination.write(stage, secrets);
86
- const shape = typeof target === "object" && "region" in target ? `{ provider: '${target.provider}', region: '${target.region}' }` : `'file'`;
87
- logger.log(`\n✓ Copied the secrets for stage "${stage}" from ${source.name} to ${destination.name}`);
88
- logger.log(`\n Next: set secrets.store to ${shape} in gkm.config.ts, so commands and deploys read from ${destination.name}.`);
89
- logger.log(` The copy in ${source.name} is left as it was; delete it once deploys read from ${destination.name}.`);
90
- }
91
- //#endregion
92
- exports.secretsMigrateCommand = secretsMigrateCommand;
93
-
94
- //# sourceMappingURL=migrate-CsMrI7Dk.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrate-CsMrI7Dk.cjs","names":["GkmError","SECRETS_STORE_PROVIDERS","UnknownSecretsStoreProvider","loadWorkspaceSettings","secretsStoreFor","storeFromConfig"],"sources":["../src/secrets/migrate.ts"],"sourcesContent":["/**\n * `gkm secrets:migrate` — copy a deployed stage's secrets from the store\n * `secrets.store` names to another one, whole: service passwords, URLs and\n * custom keys alike, so nothing is regenerated and running services keep\n * their credentials.\n *\n * It copies and nothing more. The source is left as it was, and gkm.config.ts\n * is not edited: pointing `secrets.store` at the new store is the switch, and\n * the old copy is deleted by hand once deploys read from the new one.\n */\n\nimport { loadWorkspaceSettings } from '../config.js';\nimport { GkmError } from '../errors';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport {\n\tSECRETS_STORE_PROVIDERS,\n\ttype SecretsStoreConfig,\n\ttype SecretsStoreOptions,\n\ttype SecretsStoreProvider,\n\tsecretsStoreFor,\n\tstoreFromConfig,\n\tUnknownSecretsStoreProvider,\n} from './store.js';\n\nconst logger = console;\n\nexport interface SecretsMigrateOptions {\n\tstage: string;\n\t/** The store to copy to: `'file'`, `'ssm'` or `'secrets-manager'`. */\n\tto: string;\n\t/** The target's AWS region; defaults to the configured store's. */\n\tregion?: string;\n\t/** Overwrite a stage the target already holds. */\n\tforce?: boolean;\n\t/** The AWS profile for the stage's account, for both stores. */\n\tprofile?: string;\n}\n\n/** An AWS store to copy to, with no region given and none configured. */\nexport class MigrateTargetNeedsRegion extends GkmError {\n\tconstructor(readonly to: string) {\n\t\tsuper(\n\t\t\t`Copying to ${to} needs a region, and secrets.store has none to reuse. ` +\n\t\t\t\t`Pass it: gkm secrets:migrate --to ${to} --region <region> --stage <stage>.`,\n\t\t);\n\t\tthis.name = 'MigrateTargetNeedsRegion';\n\t}\n}\n\n/** The target is the store the stage is already read from. */\nexport class MigrateTargetIsSource extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly to: string,\n\t) {\n\t\tsuper(\n\t\t\t`Stage \"${stage}\" is already kept in ${to}, so there is nothing to copy. ` +\n\t\t\t\t'Name a different store with --to, or a different region with --region.',\n\t\t);\n\t\tthis.name = 'MigrateTargetIsSource';\n\t}\n}\n\n/** The configured store holds nothing for the stage. */\nexport class NoSecretsToMigrate extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly store: string,\n\t) {\n\t\tsuper(\n\t\t\t`The ${store} store holds no secrets for stage \"${stage}\", so there is nothing to copy. ` +\n\t\t\t\t`Check secrets.store in gkm.config.ts still names the store the stage is in, ` +\n\t\t\t\t`or create the stage there with gkm secrets:init --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoSecretsToMigrate';\n\t}\n}\n\n/** The target already holds the stage, and `--force` was not given. */\nexport class MigrateTargetHoldsStage extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly to: string,\n\t) {\n\t\tsuper(\n\t\t\t`The ${to} store already holds secrets for stage \"${stage}\". ` +\n\t\t\t\t'Pass --force to replace them with the ones being copied.',\n\t\t);\n\t\tthis.name = 'MigrateTargetHoldsStage';\n\t}\n}\n\n/** The store a `--to` name and region describe. */\nfunction targetConfig(\n\tto: string,\n\tregion: string | undefined,\n\tconfigured: SecretsStoreConfig,\n): SecretsStoreConfig {\n\tif (!(SECRETS_STORE_PROVIDERS as readonly string[]).includes(to)) {\n\t\tthrow new UnknownSecretsStoreProvider(to);\n\t}\n\tconst provider = to as SecretsStoreProvider;\n\tif (provider === 'file') return 'file';\n\n\tconst fallback =\n\t\ttypeof configured === 'object' && 'region' in configured\n\t\t\t? configured.region\n\t\t\t: undefined;\n\tconst resolved = region ?? fallback;\n\tif (!resolved) throw new MigrateTargetNeedsRegion(provider);\n\treturn { provider, region: resolved };\n}\n\nexport async function secretsMigrateCommand(\n\toptions: SecretsMigrateOptions,\n): Promise<void> {\n\tconst { stage, to } = options;\n\tconst workspace = await loadWorkspaceSettings();\n\tassertDeployedStage(workspace.stages, stage);\n\n\tconst configured = workspace.secrets.store ?? 'file';\n\tconst target = targetConfig(to, options.region, configured);\n\tconst sameStore =\n\t\ttypeof configured === 'string' || typeof target === 'string'\n\t\t\t? configured === target\n\t\t\t: configured.provider === target.provider &&\n\t\t\t\t'region' in configured &&\n\t\t\t\t'region' in target &&\n\t\t\t\tconfigured.region === target.region;\n\tif (sameStore) throw new MigrateTargetIsSource(stage, to);\n\n\tconst storeOptions: SecretsStoreOptions = options.profile\n\t\t? { profile: options.profile }\n\t\t: {};\n\tconst source = await secretsStoreFor(workspace, stage, storeOptions);\n\tconst destination = await storeFromConfig(workspace, target, storeOptions);\n\n\tconst secrets = await source.read(stage);\n\tif (!secrets) throw new NoSecretsToMigrate(stage, source.name);\n\n\tif (!options.force && (await destination.read(stage))) {\n\t\tthrow new MigrateTargetHoldsStage(stage, destination.name);\n\t}\n\n\tawait destination.write(stage, secrets);\n\n\tconst shape =\n\t\ttypeof target === 'object' && 'region' in target\n\t\t\t? `{ provider: '${target.provider}', region: '${target.region}' }`\n\t\t\t: `'file'`;\n\tlogger.log(\n\t\t`\\n✓ Copied the secrets for stage \"${stage}\" from ${source.name} to ${destination.name}`,\n\t);\n\tlogger.log(\n\t\t`\\n Next: set secrets.store to ${shape} in gkm.config.ts, so commands and deploys read from ${destination.name}.`,\n\t);\n\tlogger.log(\n\t\t` The copy in ${source.name} is left as it was; delete it once deploys read from ${destination.name}.`,\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAwBA,MAAM,SAAS;;AAef,IAAa,2BAAb,cAA8CA,eAAAA,SAAS;CACjC;CAArB,YAAY,IAAqB;EAChC,MACC,cAAc,GAAG,0FACqB,GAAG,oCAC1C;EAJoB,KAAA,KAAA;EAKpB,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,wBAAb,cAA2CA,eAAAA,SAAS;CAEzC;CACA;CAFV,YACC,OACA,IACC;EACD,MACC,UAAU,MAAM,uBAAuB,GAAG,sGAE3C;EANS,KAAA,QAAA;EACA,KAAA,KAAA;EAMT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,qBAAb,cAAwCA,eAAAA,SAAS;CAEtC;CACA;CAFV,YACC,OACA,OACC;EACD,MACC,OAAO,MAAM,qCAAqC,MAAM,sKAEI,MAAM,EACnE;EAPS,KAAA,QAAA;EACA,KAAA,QAAA;EAOT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,0BAAb,cAA6CA,eAAAA,SAAS;CAE3C;CACA;CAFV,YACC,OACA,IACC;EACD,MACC,OAAO,GAAG,0CAA0C,MAAM,4DAE3D;EANS,KAAA,QAAA;EACA,KAAA,KAAA;EAMT,KAAK,OAAO;CACb;AACD;;AAGA,SAAS,aACR,IACA,QACA,YACqB;CACrB,IAAI,CAAEC,kBAAAA,wBAA8C,SAAS,EAAE,GAC9D,MAAM,IAAIC,kBAAAA,4BAA4B,EAAE;CAEzC,MAAM,WAAW;CACjB,IAAI,aAAa,QAAQ,OAAO;CAEhC,MAAM,WACL,OAAO,eAAe,YAAY,YAAY,aAC3C,WAAW,SACX,KAAA;CACJ,MAAM,WAAW,UAAU;CAC3B,IAAI,CAAC,UAAU,MAAM,IAAI,yBAAyB,QAAQ;CAC1D,OAAO;EAAE;EAAU,QAAQ;CAAS;AACrC;AAEA,eAAsB,sBACrB,SACgB;CAChB,MAAM,EAAE,OAAO,OAAO;CACtB,MAAM,YAAY,MAAMC,eAAAA,sBAAsB;CAC9C,kBAAA,oBAAoB,UAAU,QAAQ,KAAK;CAE3C,MAAM,aAAa,UAAU,QAAQ,SAAS;CAC9C,MAAM,SAAS,aAAa,IAAI,QAAQ,QAAQ,UAAU;CAQ1D,IANC,OAAO,eAAe,YAAY,OAAO,WAAW,WACjD,eAAe,SACf,WAAW,aAAa,OAAO,YAChC,YAAY,cACZ,YAAY,UACZ,WAAW,WAAW,OAAO,QACjB,MAAM,IAAI,sBAAsB,OAAO,EAAE;CAExD,MAAM,eAAoC,QAAQ,UAC/C,EAAE,SAAS,QAAQ,QAAQ,IAC3B,CAAC;CACJ,MAAM,SAAS,MAAMC,cAAAA,gBAAgB,WAAW,OAAO,YAAY;CACnE,MAAM,cAAc,MAAMC,cAAAA,gBAAgB,WAAW,QAAQ,YAAY;CAEzE,MAAM,UAAU,MAAM,OAAO,KAAK,KAAK;CACvC,IAAI,CAAC,SAAS,MAAM,IAAI,mBAAmB,OAAO,OAAO,IAAI;CAE7D,IAAI,CAAC,QAAQ,SAAU,MAAM,YAAY,KAAK,KAAK,GAClD,MAAM,IAAI,wBAAwB,OAAO,YAAY,IAAI;CAG1D,MAAM,YAAY,MAAM,OAAO,OAAO;CAEtC,MAAM,QACL,OAAO,WAAW,YAAY,YAAY,SACvC,gBAAgB,OAAO,SAAS,cAAc,OAAO,OAAO,OAC5D;CACJ,OAAO,IACN,qCAAqC,MAAM,SAAS,OAAO,KAAK,MAAM,YAAY,MACnF;CACA,OAAO,IACN,kCAAkC,MAAM,uDAAuD,YAAY,KAAK,EACjH;CACA,OAAO,IACN,iBAAiB,OAAO,KAAK,uDAAuD,YAAY,KAAK,EACtG;AACD"}
@@ -1,47 +0,0 @@
1
- const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
- const require_workspace = require("./workspace-C1M-S4al.cjs");
3
- const require_seeds = require("./seeds-BQlj9Fsg.cjs");
4
- //#region src/compose/secrets.ts
5
- var secrets_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({ deployedStackSecrets: () => deployedStackSecrets });
6
- function deployedStackSecrets(workspace, stage, stored, manifest) {
7
- const withSeed = require_seeds.withGeneratedSecrets(stored ?? require_seeds.initStageSecrets(stage), manifest);
8
- const withLogs = require_workspace.resolveStageTelemetry({
9
- ...workspace.deploy?.telemetry ? { telemetry: workspace.deploy.telemetry } : {},
10
- stage,
11
- local: false,
12
- target: "compose",
13
- runtime: "server",
14
- selfHosted: true,
15
- used: require_seeds.usesTelemetry(manifest)
16
- })?.provider === "self-hosted" ? require_seeds.withLogsPassword(withSeed.secrets) : {
17
- secrets: withSeed.secrets,
18
- generated: []
19
- };
20
- const withRedis = require_seeds.runsRedis(manifest, withLogs.secrets.custom ?? {}) ? require_seeds.withRedisPassword(withLogs.secrets) : {
21
- secrets: withLogs.secrets,
22
- generated: []
23
- };
24
- return {
25
- secrets: withRedis.secrets,
26
- generated: [
27
- ...withSeed.generated,
28
- ...withLogs.generated,
29
- ...withRedis.generated
30
- ]
31
- };
32
- }
33
- //#endregion
34
- Object.defineProperty(exports, "deployedStackSecrets", {
35
- enumerable: true,
36
- get: function() {
37
- return deployedStackSecrets;
38
- }
39
- });
40
- Object.defineProperty(exports, "secrets_exports", {
41
- enumerable: true,
42
- get: function() {
43
- return secrets_exports;
44
- }
45
- });
46
-
47
- //# sourceMappingURL=secrets-BTY6JhTp.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"secrets-BTY6JhTp.cjs","names":["withGeneratedSecrets","initStageSecrets","resolveStageTelemetry","usesTelemetry","withLogsPassword","runsRedis","withRedisPassword"],"sources":["../src/compose/secrets.ts"],"sourcesContent":["/**\n * What a deployed stage's stack generates once and keeps: its seed, each\n * `secret` construct's value, the log UI's root password where its telemetry\n * is self-hosted, and the stack's Redis password where a cache lives in it.\n *\n * Pure: the compose target writes the result back to the stage's store, and\n * `gkm deploy --resources-only` composes the stack with it to read its hosts,\n * writing nothing.\n */\n\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport {\n\ttype GeneratedSecrets,\n\twithGeneratedSecrets,\n} from '../deploy/generated.js';\nimport { initStageSecrets } from '../secrets/storage.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { resolveStageTelemetry } from '../telemetry/config';\nimport { usesTelemetry } from '../telemetry/edges';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { withLogsPassword } from './logs';\nimport { runsRedis, withRedisPassword } from './redis';\n\nexport function deployedStackSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\tstored: StageSecrets | null,\n\tmanifest: ConstructManifest,\n): GeneratedSecrets {\n\tconst withSeed = withGeneratedSecrets(\n\t\tstored ?? initStageSecrets(stage),\n\t\tmanifest,\n\t);\n\t// The log UI's root password, generated once like the seed, where the\n\t// stage's telemetry is self-hosted and the stage set none.\n\tconst telemetry = resolveStageTelemetry({\n\t\t...(workspace.deploy?.telemetry\n\t\t\t? { telemetry: workspace.deploy.telemetry }\n\t\t\t: {}),\n\t\tstage,\n\t\tlocal: false,\n\t\ttarget: 'compose',\n\t\truntime: 'server',\n\t\tselfHosted: true,\n\t\tused: usesTelemetry(manifest),\n\t});\n\tconst withLogs =\n\t\ttelemetry?.provider === 'self-hosted'\n\t\t\t? withLogsPassword(withSeed.secrets)\n\t\t\t: { secrets: withSeed.secrets, generated: [] };\n\t// The stack's Redis password, the same way, where a cache lives in it.\n\tconst withRedis = runsRedis(manifest, withLogs.secrets.custom ?? {})\n\t\t? withRedisPassword(withLogs.secrets)\n\t\t: { secrets: withLogs.secrets, generated: [] };\n\treturn {\n\t\tsecrets: withRedis.secrets,\n\t\tgenerated: [\n\t\t\t...withSeed.generated,\n\t\t\t...withLogs.generated,\n\t\t\t...withRedis.generated,\n\t\t],\n\t};\n}\n"],"mappings":";;;;;AAuBA,SAAgB,qBACf,WACA,OACA,QACA,UACmB;CACnB,MAAM,WAAWA,cAAAA,qBAChB,UAAUC,cAAAA,iBAAiB,KAAK,GAChC,QACD;CAcA,MAAM,WAXYC,kBAAAA,sBAAsB;EACvC,GAAI,UAAU,QAAQ,YACnB,EAAE,WAAW,UAAU,OAAO,UAAU,IACxC,CAAC;EACJ;EACA,OAAO;EACP,QAAQ;EACR,SAAS;EACT,YAAY;EACZ,MAAMC,cAAAA,cAAc,QAAQ;CAC7B,CAES,CAAC,EAAE,aAAa,gBACrBC,cAAAA,iBAAiB,SAAS,OAAO,IACjC;EAAE,SAAS,SAAS;EAAS,WAAW,CAAC;CAAE;CAE/C,MAAM,YAAYC,cAAAA,UAAU,UAAU,SAAS,QAAQ,UAAU,CAAC,CAAC,IAChEC,cAAAA,kBAAkB,SAAS,OAAO,IAClC;EAAE,SAAS,SAAS;EAAS,WAAW,CAAC;CAAE;CAC9C,OAAO;EACN,SAAS,UAAU;EACnB,WAAW;GACV,GAAG,SAAS;GACZ,GAAG,SAAS;GACZ,GAAG,UAAU;EACd;CACD;AACD"}
@@ -1,36 +0,0 @@
1
- import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
- import { L as resolveStageTelemetry } from "./workspace-Bv4xROeu.mjs";
3
- import { St as withRedisPassword, Wn as initStageSecrets, bt as runsRedis, er as withLogsPassword, fn as usesTelemetry, gt as withGeneratedSecrets } from "./seeds-BsJRUL1K.mjs";
4
- //#region src/compose/secrets.ts
5
- var secrets_exports = /* @__PURE__ */ __exportAll({ deployedStackSecrets: () => deployedStackSecrets });
6
- function deployedStackSecrets(workspace, stage, stored, manifest) {
7
- const withSeed = withGeneratedSecrets(stored ?? initStageSecrets(stage), manifest);
8
- const withLogs = resolveStageTelemetry({
9
- ...workspace.deploy?.telemetry ? { telemetry: workspace.deploy.telemetry } : {},
10
- stage,
11
- local: false,
12
- target: "compose",
13
- runtime: "server",
14
- selfHosted: true,
15
- used: usesTelemetry(manifest)
16
- })?.provider === "self-hosted" ? withLogsPassword(withSeed.secrets) : {
17
- secrets: withSeed.secrets,
18
- generated: []
19
- };
20
- const withRedis = runsRedis(manifest, withLogs.secrets.custom ?? {}) ? withRedisPassword(withLogs.secrets) : {
21
- secrets: withLogs.secrets,
22
- generated: []
23
- };
24
- return {
25
- secrets: withRedis.secrets,
26
- generated: [
27
- ...withSeed.generated,
28
- ...withLogs.generated,
29
- ...withRedis.generated
30
- ]
31
- };
32
- }
33
- //#endregion
34
- export { secrets_exports as n, deployedStackSecrets as t };
35
-
36
- //# sourceMappingURL=secrets-HzKNTWg9.mjs.map