@geekmidas/cli 10.0.0-alpha.4 → 10.0.0-alpha.40

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 (257) hide show
  1. package/README.md +52 -74
  2. package/bin/adjacent-tsconfig.mjs +97 -0
  3. package/bin/gkm.mjs +7 -1
  4. package/dist/{HostingerProvider-h3EbEp-Z.cjs → HostingerProvider-BZFGYYVA.cjs} +21 -22
  5. package/dist/HostingerProvider-BZFGYYVA.cjs.map +1 -0
  6. package/dist/{HostingerProvider-Cft7lEYr.mjs → HostingerProvider-Bh6K7QYz.mjs} +20 -21
  7. package/dist/HostingerProvider-Bh6K7QYz.mjs.map +1 -0
  8. package/dist/{LocalStateProvider-Db9SEjyH.cjs → LocalStateProvider-7HR6SS2t.cjs} +11 -6
  9. package/dist/LocalStateProvider-7HR6SS2t.cjs.map +1 -0
  10. package/dist/{LocalStateProvider-Bn6tS-oL.mjs → LocalStateProvider-D7QyJoLM.mjs} +10 -4
  11. package/dist/LocalStateProvider-D7QyJoLM.mjs.map +1 -0
  12. package/dist/{openapi-CA0yJUTN.cjs → OpenApiTsGenerator-B4Kj_qNU.cjs} +142 -303
  13. package/dist/OpenApiTsGenerator-B4Kj_qNU.cjs.map +1 -0
  14. package/dist/{openapi-kkQbsRVj.mjs → OpenApiTsGenerator-CEh3EKN3.mjs} +113 -255
  15. package/dist/OpenApiTsGenerator-CEh3EKN3.mjs.map +1 -0
  16. package/dist/{Route53Provider-RdpUDJQk.mjs → Route53Provider-Cvj40A8I.mjs} +12 -12
  17. package/dist/Route53Provider-Cvj40A8I.mjs.map +1 -0
  18. package/dist/{Route53Provider-di2fMM5N.cjs → Route53Provider-DVILBsvV.cjs} +20 -21
  19. package/dist/Route53Provider-DVILBsvV.cjs.map +1 -0
  20. package/dist/{SSMStateProvider-BRt2py-A.mjs → SSMStateProvider-B0Hu1MKZ.mjs} +13 -4
  21. package/dist/SSMStateProvider-B0Hu1MKZ.mjs.map +1 -0
  22. package/dist/{SSMStateProvider-DyX8s9HI.cjs → SSMStateProvider-VrQ-yeqd.cjs} +17 -9
  23. package/dist/SSMStateProvider-VrQ-yeqd.cjs.map +1 -0
  24. package/dist/aws-DkHdTV-p.cjs +61 -0
  25. package/dist/aws-DkHdTV-p.cjs.map +1 -0
  26. package/dist/aws-DojzQnLd.mjs +61 -0
  27. package/dist/aws-DojzQnLd.mjs.map +1 -0
  28. package/dist/{backup-provisioner-BrsHwYG0.cjs → backup-provisioner-BmhIO_Yr.cjs} +21 -19
  29. package/dist/backup-provisioner-BmhIO_Yr.cjs.map +1 -0
  30. package/dist/{backup-provisioner-Bgj5Vi_L.mjs → backup-provisioner-DIVCbfee.mjs} +10 -7
  31. package/dist/backup-provisioner-DIVCbfee.mjs.map +1 -0
  32. package/dist/{bundler-Be8Zg4Wd.mjs → bundler--fyybf1N.mjs} +19 -30
  33. package/dist/bundler--fyybf1N.mjs.map +1 -0
  34. package/dist/{bundler-BY1OmV1e.cjs → bundler-By6_sXiL.cjs} +20 -32
  35. package/dist/bundler-By6_sXiL.cjs.map +1 -0
  36. package/dist/{config-WRdsQYZK.mjs → config-5watB1D0.mjs} +142 -154
  37. package/dist/config-5watB1D0.mjs.map +1 -0
  38. package/dist/{config-C21ocYNd.cjs → config-CJ-PAb7M.cjs} +209 -210
  39. package/dist/config-CJ-PAb7M.cjs.map +1 -0
  40. package/dist/config.cjs +9 -6
  41. package/dist/config.d.cts +62 -33
  42. package/dist/config.d.cts.map +1 -1
  43. package/dist/config.d.mts +62 -33
  44. package/dist/config.d.mts.map +1 -1
  45. package/dist/config.mjs +3 -5
  46. package/dist/deploy/sniffer-envkit-patch.cjs +18 -10
  47. package/dist/deploy/sniffer-envkit-patch.cjs.map +1 -1
  48. package/dist/deploy/sniffer-envkit-patch.d.cts +0 -2
  49. package/dist/deploy/sniffer-envkit-patch.d.cts.map +1 -1
  50. package/dist/deploy/sniffer-envkit-patch.d.mts +0 -2
  51. package/dist/deploy/sniffer-envkit-patch.d.mts.map +1 -1
  52. package/dist/deploy/sniffer-envkit-patch.mjs +10 -2
  53. package/dist/deploy/sniffer-envkit-patch.mjs.map +1 -1
  54. package/dist/deploy/sniffer-hooks.cjs +13 -7
  55. package/dist/deploy/sniffer-hooks.cjs.map +1 -1
  56. package/dist/deploy/sniffer-hooks.d.cts +1 -2
  57. package/dist/deploy/sniffer-hooks.d.cts.map +1 -1
  58. package/dist/deploy/sniffer-hooks.d.mts +1 -2
  59. package/dist/deploy/sniffer-hooks.d.mts.map +1 -1
  60. package/dist/deploy/sniffer-hooks.mjs +9 -2
  61. package/dist/deploy/sniffer-hooks.mjs.map +1 -1
  62. package/dist/deploy/sniffer-loader.cjs +16 -7
  63. package/dist/deploy/sniffer-loader.cjs.map +1 -1
  64. package/dist/deploy/sniffer-loader.d.cts +1 -1
  65. package/dist/deploy/sniffer-loader.d.mts +1 -1
  66. package/dist/deploy/sniffer-loader.mjs +13 -2
  67. package/dist/deploy/sniffer-loader.mjs.map +1 -1
  68. package/dist/deploy/sniffer-routes-worker.cjs +11 -11
  69. package/dist/deploy/sniffer-routes-worker.cjs.map +1 -1
  70. package/dist/deploy/sniffer-routes-worker.d.cts +1 -1
  71. package/dist/deploy/sniffer-routes-worker.d.mts +1 -1
  72. package/dist/deploy/sniffer-routes-worker.mjs +3 -3
  73. package/dist/deploy/sniffer-routes-worker.mjs.map +1 -1
  74. package/dist/deploy/sniffer-worker.cjs +15 -6
  75. package/dist/deploy/sniffer-worker.cjs.map +1 -1
  76. package/dist/deploy/sniffer-worker.d.cts +1 -3
  77. package/dist/deploy/sniffer-worker.d.cts.map +1 -1
  78. package/dist/deploy/sniffer-worker.d.mts +1 -3
  79. package/dist/deploy/sniffer-worker.d.mts.map +1 -1
  80. package/dist/deploy/sniffer-worker.mjs +15 -4
  81. package/dist/deploy/sniffer-worker.mjs.map +1 -1
  82. package/dist/{discover-DTmFeloL.cjs → discover-B0f_kuNN.cjs} +118 -66
  83. package/dist/discover-B0f_kuNN.cjs.map +1 -0
  84. package/dist/{discover-CAlFgYec.mjs → discover-BLhf7V_J.mjs} +67 -22
  85. package/dist/discover-BLhf7V_J.mjs.map +1 -0
  86. package/dist/{emit-CbcRolsa.cjs → emit-BdoPFjZf.cjs} +53 -36
  87. package/dist/emit-BdoPFjZf.cjs.map +1 -0
  88. package/dist/{emit-CiZS_5Ub.mjs → emit-Bzq7JsMP.mjs} +24 -6
  89. package/dist/emit-Bzq7JsMP.mjs.map +1 -0
  90. package/dist/github-BAr8WGkR.cjs +213 -0
  91. package/dist/github-BAr8WGkR.cjs.map +1 -0
  92. package/dist/github-D5MthUlR.mjs +213 -0
  93. package/dist/github-D5MthUlR.mjs.map +1 -0
  94. package/dist/{index-DCtipn2h.d.cts → index-B3PeGOQp.d.cts} +4 -23
  95. package/dist/index-B3PeGOQp.d.cts.map +1 -0
  96. package/dist/{index-BThUzEIp.d.mts → index-BOyaGcCm.d.mts} +4 -23
  97. package/dist/index-BOyaGcCm.d.mts.map +1 -0
  98. package/dist/index.cjs +7071 -4519
  99. package/dist/index.cjs.map +1 -1
  100. package/dist/index.d.cts +1 -1
  101. package/dist/index.d.mts +1 -1
  102. package/dist/index.mjs +7117 -4586
  103. package/dist/index.mjs.map +1 -1
  104. package/dist/migrate-BVI3yqcE.mjs +736 -0
  105. package/dist/migrate-BVI3yqcE.mjs.map +1 -0
  106. package/dist/migrate-CFOUApUt.cjs +743 -0
  107. package/dist/migrate-CFOUApUt.cjs.map +1 -0
  108. package/dist/openapi.cjs +197 -8
  109. package/dist/openapi.cjs.map +1 -0
  110. package/dist/openapi.d.cts +47 -14
  111. package/dist/openapi.d.cts.map +1 -1
  112. package/dist/openapi.d.mts +47 -14
  113. package/dist/openapi.d.mts.map +1 -1
  114. package/dist/openapi.mjs +191 -5
  115. package/dist/openapi.mjs.map +1 -0
  116. package/dist/ready-D8UXsP6q.mjs +516 -0
  117. package/dist/ready-D8UXsP6q.mjs.map +1 -0
  118. package/dist/ready-mcxamf73.cjs +563 -0
  119. package/dist/ready-mcxamf73.cjs.map +1 -0
  120. package/dist/reconcile/public.cjs +4 -4
  121. package/dist/reconcile/public.d.cts +27 -26
  122. package/dist/reconcile/public.d.cts.map +1 -1
  123. package/dist/reconcile/public.d.mts +27 -26
  124. package/dist/reconcile/public.d.mts.map +1 -1
  125. package/dist/reconcile/public.mjs +3 -4
  126. package/dist/{reconcile-CFs8C_1a.mjs → reconcile--I7V4Klj.mjs} +4 -7
  127. package/dist/reconcile--I7V4Klj.mjs.map +1 -0
  128. package/dist/{reconcile-D0pimNiy.cjs → reconcile-CuCQBa5-.cjs} +5 -8
  129. package/dist/reconcile-CuCQBa5-.cjs.map +1 -0
  130. package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
  131. package/dist/{chunk-CUT6urMc.cjs → rolldown-runtime-C0BPl7ul.cjs} +23 -10
  132. package/dist/storage-BzaUxmqe.mjs +124 -0
  133. package/dist/storage-BzaUxmqe.mjs.map +1 -0
  134. package/dist/storage-CSGoC3bK.cjs +152 -0
  135. package/dist/storage-CSGoC3bK.cjs.map +1 -0
  136. package/dist/store-DQgxT_WR.cjs +211 -0
  137. package/dist/store-DQgxT_WR.cjs.map +1 -0
  138. package/dist/store-DeGATCS-.mjs +182 -0
  139. package/dist/store-DeGATCS-.mjs.map +1 -0
  140. package/dist/types-0R124hrY.d.mts +6324 -0
  141. package/dist/types-0R124hrY.d.mts.map +1 -0
  142. package/dist/types-DfdYe31u.d.cts +6325 -0
  143. package/dist/types-DfdYe31u.d.cts.map +1 -0
  144. package/dist/vitest.cjs +65 -0
  145. package/dist/vitest.cjs.map +1 -0
  146. package/dist/vitest.d.cts +29 -0
  147. package/dist/vitest.d.cts.map +1 -0
  148. package/dist/vitest.d.mts +29 -0
  149. package/dist/vitest.d.mts.map +1 -0
  150. package/dist/vitest.mjs +65 -0
  151. package/dist/vitest.mjs.map +1 -0
  152. package/dist/workspace/index.cjs +3 -4
  153. package/dist/workspace/index.d.cts +3 -3
  154. package/dist/workspace/index.d.mts +3 -3
  155. package/dist/workspace/index.mjs +2 -3
  156. package/dist/{credentials-G1JwBoXH.cjs → workspace-BIqeeqcR.cjs} +2248 -1809
  157. package/dist/workspace-BIqeeqcR.cjs.map +1 -0
  158. package/dist/{workspace-BFMrAKqI.mjs → workspace-DOmIhiau.mjs} +863 -952
  159. package/dist/workspace-DOmIhiau.mjs.map +1 -0
  160. package/dist/{credentials-BBIRJVp4.mjs → workspace-DwS1JK5u.mjs} +2114 -1706
  161. package/dist/workspace-DwS1JK5u.mjs.map +1 -0
  162. package/dist/{workspace-BcucjwvA.cjs → workspace-RYSYtHP7.cjs} +1032 -1098
  163. package/dist/workspace-RYSYtHP7.cjs.map +1 -0
  164. package/package.json +29 -20
  165. package/dist/CachedStateProvider-BLo7zv7S.cjs +0 -3
  166. package/dist/CachedStateProvider-BxoAm-B2.mjs +0 -3
  167. package/dist/CachedStateProvider-CqPdW7CB.cjs +0 -60
  168. package/dist/CachedStateProvider-CqPdW7CB.cjs.map +0 -1
  169. package/dist/CachedStateProvider-DTniehMP.mjs +0 -54
  170. package/dist/CachedStateProvider-DTniehMP.mjs.map +0 -1
  171. package/dist/HostingerProvider-Cft7lEYr.mjs.map +0 -1
  172. package/dist/HostingerProvider-h3EbEp-Z.cjs.map +0 -1
  173. package/dist/LocalStateProvider-Bn6tS-oL.mjs.map +0 -1
  174. package/dist/LocalStateProvider-Db9SEjyH.cjs.map +0 -1
  175. package/dist/Route53Provider-RdpUDJQk.mjs.map +0 -1
  176. package/dist/Route53Provider-di2fMM5N.cjs.map +0 -1
  177. package/dist/SSMStateProvider-BRt2py-A.mjs.map +0 -1
  178. package/dist/SSMStateProvider-DyX8s9HI.cjs.map +0 -1
  179. package/dist/backup-provisioner-Bgj5Vi_L.mjs.map +0 -1
  180. package/dist/backup-provisioner-BrsHwYG0.cjs.map +0 -1
  181. package/dist/bundler-BY1OmV1e.cjs.map +0 -1
  182. package/dist/bundler-Be8Zg4Wd.mjs.map +0 -1
  183. package/dist/chunk-Duj1WY3L.mjs +0 -7
  184. package/dist/config-C21ocYNd.cjs.map +0 -1
  185. package/dist/config-WRdsQYZK.mjs.map +0 -1
  186. package/dist/credentials-B5zRHdAl.mjs +0 -141
  187. package/dist/credentials-B5zRHdAl.mjs.map +0 -1
  188. package/dist/credentials-BBIRJVp4.mjs.map +0 -1
  189. package/dist/credentials-BapzjyV5.cjs +0 -195
  190. package/dist/credentials-BapzjyV5.cjs.map +0 -1
  191. package/dist/credentials-G1JwBoXH.cjs.map +0 -1
  192. package/dist/discover-CAlFgYec.mjs.map +0 -1
  193. package/dist/discover-DTmFeloL.cjs.map +0 -1
  194. package/dist/dokploy-api-BS2xnJLZ.mjs +0 -3
  195. package/dist/dokploy-api-BsVH0y_s.cjs +0 -718
  196. package/dist/dokploy-api-BsVH0y_s.cjs.map +0 -1
  197. package/dist/dokploy-api-BuxDRo3l.cjs +0 -3
  198. package/dist/dokploy-api-aTFPz7_4.mjs +0 -706
  199. package/dist/dokploy-api-aTFPz7_4.mjs.map +0 -1
  200. package/dist/emit-CbcRolsa.cjs.map +0 -1
  201. package/dist/emit-CiZS_5Ub.mjs.map +0 -1
  202. package/dist/encryption-C6jaU0hE.cjs +0 -54
  203. package/dist/encryption-C6jaU0hE.cjs.map +0 -1
  204. package/dist/encryption-CHzzeR8a.cjs +0 -4
  205. package/dist/encryption-CQUncu7v.mjs +0 -3
  206. package/dist/encryption-D-abyhZx.mjs +0 -42
  207. package/dist/encryption-D-abyhZx.mjs.map +0 -1
  208. package/dist/fullstack-secrets-C-GGohke.cjs +0 -111
  209. package/dist/fullstack-secrets-C-GGohke.cjs.map +0 -1
  210. package/dist/fullstack-secrets-D1o9-G5l.mjs +0 -87
  211. package/dist/fullstack-secrets-D1o9-G5l.mjs.map +0 -1
  212. package/dist/generator-CNzBw7g5.cjs +0 -289
  213. package/dist/generator-CNzBw7g5.cjs.map +0 -1
  214. package/dist/generator-VE2JrFlY.mjs +0 -253
  215. package/dist/generator-VE2JrFlY.mjs.map +0 -1
  216. package/dist/index-BThUzEIp.d.mts.map +0 -1
  217. package/dist/index-DCtipn2h.d.cts.map +0 -1
  218. package/dist/openapi-CA0yJUTN.cjs.map +0 -1
  219. package/dist/openapi-kkQbsRVj.mjs.map +0 -1
  220. package/dist/openapi-react-query-Byltvm_7.mjs +0 -167
  221. package/dist/openapi-react-query-Byltvm_7.mjs.map +0 -1
  222. package/dist/openapi-react-query-CmJ1xDbn.cjs +0 -173
  223. package/dist/openapi-react-query-CmJ1xDbn.cjs.map +0 -1
  224. package/dist/openapi-react-query.cjs +0 -4
  225. package/dist/openapi-react-query.d.cts +0 -11
  226. package/dist/openapi-react-query.d.cts.map +0 -1
  227. package/dist/openapi-react-query.d.mts +0 -11
  228. package/dist/openapi-react-query.d.mts.map +0 -1
  229. package/dist/openapi-react-query.mjs +0 -4
  230. package/dist/reconcile-CFs8C_1a.mjs.map +0 -1
  231. package/dist/reconcile-D0pimNiy.cjs.map +0 -1
  232. package/dist/setup-B5_oHHUj.cjs +0 -11
  233. package/dist/setup-Cm2UnAdh.cjs +0 -473
  234. package/dist/setup-Cm2UnAdh.cjs.map +0 -1
  235. package/dist/setup-DXvRLTZN.mjs +0 -443
  236. package/dist/setup-DXvRLTZN.mjs.map +0 -1
  237. package/dist/setup-ZijS4qeC.mjs +0 -11
  238. package/dist/storage-BJmzYxt8.cjs +0 -363
  239. package/dist/storage-BJmzYxt8.cjs.map +0 -1
  240. package/dist/storage-BM9hGpgv.cjs +0 -7
  241. package/dist/storage-Bi24WfoW.mjs +0 -297
  242. package/dist/storage-Bi24WfoW.mjs.map +0 -1
  243. package/dist/storage-CYlGwWTj.mjs +0 -3
  244. package/dist/sync-CmEuNZsB.mjs +0 -76
  245. package/dist/sync-CmEuNZsB.mjs.map +0 -1
  246. package/dist/sync-D306yOWu.cjs +0 -4
  247. package/dist/sync-SizaChmC.cjs +0 -93
  248. package/dist/sync-SizaChmC.cjs.map +0 -1
  249. package/dist/sync-hOqAWHR6.mjs +0 -3
  250. package/dist/types-BhIl-COG.d.cts +0 -2184
  251. package/dist/types-BhIl-COG.d.cts.map +0 -1
  252. package/dist/types-C-arnQ1r.d.mts +0 -2184
  253. package/dist/types-C-arnQ1r.d.mts.map +0 -1
  254. package/dist/workspace-BFMrAKqI.mjs.map +0 -1
  255. package/dist/workspace-BcucjwvA.cjs.map +0 -1
  256. package/dist/workspace-DdJss8I6.cjs +0 -9
  257. package/dist/workspace-k4l3rsFh.mjs +0 -8
@@ -0,0 +1,213 @@
1
+ const require_config = require("./config-CJ-PAb7M.cjs");
2
+ const require_workspace = require("./workspace-RYSYtHP7.cjs");
3
+ const require_store = require("./store-DQgxT_WR.cjs");
4
+ let node_fs = require("node:fs");
5
+ let node_child_process = require("node:child_process");
6
+ let _aws_sdk_client_iam = require("@aws-sdk/client-iam");
7
+ //#region src/deploy/github.ts
8
+ /**
9
+ * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.
10
+ *
11
+ * A stage usually lives in its own AWS account — staging in one, production in
12
+ * another — so this runs once per stage, with that account's profile:
13
+ *
14
+ * gkm deploy:github --stage staging --profile acme-dev
15
+ * gkm deploy:github --stage prod --profile acme-prod
16
+ *
17
+ * In the stage's account it makes sure GitHub's OIDC provider exists, and a
18
+ * role that only this repository's `<stage>` environment can assume. On
19
+ * GitHub it creates that environment and gives it what the generated deploy
20
+ * workflow reads: `AWS_ROLE_ARN`, and — when the stage's secrets are kept in
21
+ * the local file — its key as `GKM_SECRETS_KEY`. With `secrets.store` set to
22
+ * SSM the deploy job pulls them with the role instead, so no key is handed to
23
+ * GitHub, and this pushes the stage's local secrets to SSM in the same account,
24
+ * with the same profile. Re-running it converges rather than duplicating.
25
+ */
26
+ const logger = console;
27
+ const GITHUB_OIDC_HOST = "token.actions.githubusercontent.com";
28
+ const AUDIENCE = "sts.amazonaws.com";
29
+ const defaultGh = (args, input) => (0, node_child_process.execFileSync)("gh", args, {
30
+ encoding: "utf-8",
31
+ ...input === void 0 ? {} : {
32
+ input,
33
+ stdio: [
34
+ "pipe",
35
+ "pipe",
36
+ "pipe"
37
+ ]
38
+ }
39
+ }).trim();
40
+ /** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */
41
+ function roleName(project, stage) {
42
+ return `${project}-github-${stage}`.toLowerCase().replace(/[^a-z0-9+=,.@_-]/g, "-").slice(0, 64);
43
+ }
44
+ /**
45
+ * Who may assume the role: a workflow job running in this repository's
46
+ * `<stage>` environment, and nothing else — not another branch's job, not
47
+ * another environment's. That is what keeps a staging deploy from using the
48
+ * production role.
49
+ */
50
+ function trustPolicy(providerArn, repo, stage) {
51
+ return JSON.stringify({
52
+ Version: "2012-10-17",
53
+ Statement: [{
54
+ Effect: "Allow",
55
+ Principal: { Federated: providerArn },
56
+ Action: "sts:AssumeRoleWithWebIdentity",
57
+ Condition: { StringEquals: {
58
+ [`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,
59
+ [`${GITHUB_OIDC_HOST}:sub`]: `repo:${repo}:environment:${stage}`
60
+ } }
61
+ }]
62
+ });
63
+ }
64
+ /** GitHub's OIDC provider in this account, created if it is not there. */
65
+ async function ensureOidcProvider(iam) {
66
+ const { OpenIDConnectProviderList = [] } = await iam.send(new _aws_sdk_client_iam.ListOpenIDConnectProvidersCommand({}));
67
+ const existing = OpenIDConnectProviderList.find((p) => p.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`))?.Arn;
68
+ if (existing) return existing;
69
+ return (await iam.send(new _aws_sdk_client_iam.CreateOpenIDConnectProviderCommand({
70
+ Url: `https://${GITHUB_OIDC_HOST}`,
71
+ ClientIDList: [AUDIENCE]
72
+ }))).OpenIDConnectProviderArn;
73
+ }
74
+ /** The deploy role, created or brought back to this trust and policy. */
75
+ async function ensureRole(iam, name, trust, policyArn) {
76
+ let arn;
77
+ try {
78
+ const { Role } = await iam.send(new _aws_sdk_client_iam.GetRoleCommand({ RoleName: name }));
79
+ await iam.send(new _aws_sdk_client_iam.UpdateAssumeRolePolicyCommand({
80
+ RoleName: name,
81
+ PolicyDocument: trust
82
+ }));
83
+ arn = Role.Arn;
84
+ } catch (error) {
85
+ if (error.name !== "NoSuchEntityException") throw error;
86
+ const { Role } = await iam.send(new _aws_sdk_client_iam.CreateRoleCommand({
87
+ RoleName: name,
88
+ AssumeRolePolicyDocument: trust,
89
+ Description: "Deploys from GitHub Actions (gkm deploy:github)",
90
+ MaxSessionDuration: 7200
91
+ }));
92
+ arn = Role.Arn;
93
+ }
94
+ await iam.send(new _aws_sdk_client_iam.AttachRolePolicyCommand({
95
+ RoleName: name,
96
+ PolicyArn: policyArn
97
+ }));
98
+ return arn;
99
+ }
100
+ /**
101
+ * An IAM client for the stage's account.
102
+ *
103
+ * `fromIni` for a named profile, deliberately: it resolves exactly that
104
+ * profile — keys, assume-role, `credential_process` or SSO, whichever
105
+ * ~/.aws/config says — and never falls back to AWS_* in the environment. The
106
+ * provider chain checks the environment first, so exported staging keys would
107
+ * quietly win over `--profile prod` and the production role would land in the
108
+ * staging account.
109
+ */
110
+ async function iamFor(profile, endpoint) {
111
+ const config = { region: "us-east-1" };
112
+ if (profile) {
113
+ const { fromIni } = await import("@aws-sdk/credential-providers");
114
+ config.credentials = fromIni({ profile });
115
+ }
116
+ if (endpoint) {
117
+ config.endpoint = endpoint;
118
+ config.credentials = {
119
+ accessKeyId: "test",
120
+ secretAccessKey: "test"
121
+ };
122
+ }
123
+ return new _aws_sdk_client_iam.IAMClient(config);
124
+ }
125
+ async function deployGithubCommand(options, deps = {}) {
126
+ const gh = deps.gh ?? defaultGh;
127
+ const { workspace } = await require_config.loadWorkspaceConfig(deps.cwd);
128
+ require_workspace.assertDeployedStage(workspace.stages, options.stage);
129
+ const repo = options.repo ?? gh([
130
+ "repo",
131
+ "view",
132
+ "--json",
133
+ "nameWithOwner",
134
+ "-q",
135
+ ".nameWithOwner"
136
+ ]);
137
+ const role = roleName(workspace.name, options.stage);
138
+ const policyArn = options.policyArn ?? "arn:aws:iam::aws:policy/AdministratorAccess";
139
+ const keyPath = require_store.getKeyPath(options.stage, workspace.name);
140
+ const hasKey = (0, node_fs.existsSync)(keyPath);
141
+ const remote = require_store.isRemoteStore(workspace, options.stage);
142
+ const store = await require_store.secretsStoreFor(workspace, options.stage, { ...options.profile ? { profile: options.profile } : {} });
143
+ const hasSecrets = remote && await store.read(options.stage) !== null;
144
+ const via = options.profile ? `profile "${options.profile}"` : "the default credentials";
145
+ logger.log(`\n🔐 GitHub → AWS for stage "${options.stage}"\n`);
146
+ logger.log(` Repository: ${repo}`);
147
+ logger.log(` AWS profile: ${options.profile ?? "(default credentials)"}`);
148
+ logger.log(` Role: ${role}`);
149
+ logger.log(` Policy: ${policyArn}${options.policyArn ? "" : " (default; --policy-arn to narrow)"}`);
150
+ logger.log(` Trusted by: repo:${repo}:environment:${options.stage} only`);
151
+ if (remote) logger.log(` Secrets: ${hasSecrets ? `in the store (${store.name}), read with ${via}` : `none in the store yet — gkm secrets:set <KEY> '…' --stage ${options.stage}`}`);
152
+ else logger.log(` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`);
153
+ if (options.dryRun) {
154
+ logger.log("\n --dry-run: nothing changed.\n");
155
+ return { repo };
156
+ }
157
+ const iam = deps.iam ?? await iamFor(options.profile);
158
+ const roleArn = await ensureRole(iam, role, trustPolicy(await ensureOidcProvider(iam).catch((error) => {
159
+ if (/SSO session/i.test(String(error?.message)) && options.profile) throw new SsoSessionExpired(options.profile);
160
+ throw error;
161
+ }), repo, options.stage), policyArn);
162
+ logger.log(`\n ✓ AWS: ${roleArn}`);
163
+ gh([
164
+ "api",
165
+ "--method",
166
+ "PUT",
167
+ `repos/${repo}/environments/${options.stage}`
168
+ ]);
169
+ gh([
170
+ "variable",
171
+ "set",
172
+ "AWS_ROLE_ARN",
173
+ "--env",
174
+ options.stage,
175
+ "--repo",
176
+ repo,
177
+ "--body",
178
+ roleArn
179
+ ]);
180
+ logger.log(` ✓ GitHub: environment "${options.stage}", AWS_ROLE_ARN`);
181
+ if (remote) {
182
+ if (!hasSecrets) logger.log(` ⚠ The "${options.stage}" store holds no secrets yet; set them with gkm secrets:set <KEY> '…' --stage ${options.stage}${options.profile ? ` (AWS_PROFILE=${options.profile})` : ""}.`);
183
+ } else if (hasKey) {
184
+ gh([
185
+ "secret",
186
+ "set",
187
+ "GKM_SECRETS_KEY",
188
+ "--env",
189
+ options.stage,
190
+ "--repo",
191
+ repo
192
+ ], (0, node_fs.readFileSync)(keyPath, "utf-8").trim());
193
+ logger.log(" ✓ GitHub: GKM_SECRETS_KEY");
194
+ } else logger.log(` ⚠ No secrets key for "${options.stage}"; run gkm secrets:init --stage ${options.stage}, then this again.`);
195
+ logger.log(`\n Protect it: add required reviewers to the "${options.stage}" environment in the repository settings.\n`);
196
+ return {
197
+ roleArn,
198
+ repo
199
+ };
200
+ }
201
+ /** The profile's SSO login has lapsed; the fix is one command away. */
202
+ var SsoSessionExpired = class extends Error {
203
+ profile;
204
+ constructor(profile) {
205
+ super(`The SSO session for profile "${profile}" has expired or is invalid. Run: aws sso login --profile ${profile}`);
206
+ this.profile = profile;
207
+ this.name = "SsoSessionExpired";
208
+ }
209
+ };
210
+ //#endregion
211
+ exports.deployGithubCommand = deployGithubCommand;
212
+
213
+ //# sourceMappingURL=github-BAr8WGkR.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-BAr8WGkR.cjs","names":["execFileSync","ListOpenIDConnectProvidersCommand","CreateOpenIDConnectProviderCommand","GetRoleCommand","UpdateAssumeRolePolicyCommand","CreateRoleCommand","AttachRolePolicyCommand","IAMClient","loadWorkspaceConfig","getKeyPath","existsSync","isRemoteStore","secretsStoreFor","readFileSync"],"sources":["../src/deploy/github.ts"],"sourcesContent":["/**\n * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.\n *\n * A stage usually lives in its own AWS account — staging in one, production in\n * another — so this runs once per stage, with that account's profile:\n *\n * gkm deploy:github --stage staging --profile acme-dev\n * gkm deploy:github --stage prod --profile acme-prod\n *\n * In the stage's account it makes sure GitHub's OIDC provider exists, and a\n * role that only this repository's `<stage>` environment can assume. On\n * GitHub it creates that environment and gives it what the generated deploy\n * workflow reads: `AWS_ROLE_ARN`, and — when the stage's secrets are kept in\n * the local file — its key as `GKM_SECRETS_KEY`. With `secrets.store` set to\n * SSM the deploy job pulls them with the role instead, so no key is handed to\n * GitHub, and this pushes the stage's local secrets to SSM in the same account,\n * with the same profile. Re-running it converges rather than duplicating.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListOpenIDConnectProvidersCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { getKeyPath } from '../secrets/keystore.js';\nimport { isRemoteStore, secretsStoreFor } from '../secrets/store.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default —\n * said out loud in the output, and replaceable with `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\nexport interface DeployGithubOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account. */\n\tprofile?: string;\n\t/** `owner/name`; defaults to the repository `gh` sees here. */\n\trepo?: string;\n\t/** The policy the deploy role gets. */\n\tpolicyArn?: string;\n\tdryRun?: boolean;\n}\n\n/** Runs `gh` with the given arguments and optional stdin, returning stdout. */\nexport type Gh = (args: string[], input?: string) => string;\n\nconst defaultGh: Gh = (args, input) =>\n\texecFileSync('gh', args, {\n\t\tencoding: 'utf-8',\n\t\t...(input === undefined\n\t\t\t? {}\n\t\t\t: { input, stdio: ['pipe', 'pipe', 'pipe'] as const }),\n\t}).trim();\n\n/** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */\nexport function roleName(project: string, stage: string): string {\n\treturn `${project}-github-${stage}`\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9+=,.@_-]/g, '-')\n\t\t.slice(0, 64);\n}\n\n/**\n * Who may assume the role: a workflow job running in this repository's\n * `<stage>` environment, and nothing else — not another branch's job, not\n * another environment's. That is what keeps a staging deploy from using the\n * production role.\n */\nexport function trustPolicy(\n\tproviderArn: string,\n\trepo: string,\n\tstage: string,\n): string {\n\treturn JSON.stringify({\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tPrincipal: { Federated: providerArn },\n\t\t\t\tAction: 'sts:AssumeRoleWithWebIdentity',\n\t\t\t\tCondition: {\n\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:sub`]: `repo:${repo}:environment:${stage}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/** GitHub's OIDC provider in this account, created if it is not there. */\nexport async function ensureOidcProvider(iam: IAMClient): Promise<string> {\n\tconst { OpenIDConnectProviderList = [] } = await iam.send(\n\t\tnew ListOpenIDConnectProvidersCommand({}),\n\t);\n\tconst existing = OpenIDConnectProviderList.find((p) =>\n\t\tp.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`),\n\t)?.Arn;\n\tif (existing) return existing;\n\n\tconst created = await iam.send(\n\t\tnew CreateOpenIDConnectProviderCommand({\n\t\t\tUrl: `https://${GITHUB_OIDC_HOST}`,\n\t\t\tClientIDList: [AUDIENCE],\n\t\t}),\n\t);\n\treturn created.OpenIDConnectProviderArn!;\n}\n\n/** The deploy role, created or brought back to this trust and policy. */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\tpolicyArn: string,\n): Promise<string> {\n\tlet arn: string;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tawait iam.send(\n\t\t\tnew UpdateAssumeRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyDocument: trust,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name !== 'NoSuchEntityException') {\n\t\t\tthrow error;\n\t\t}\n\t\tconst { Role } = await iam.send(\n\t\t\tnew CreateRoleCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tAssumeRolePolicyDocument: trust,\n\t\t\t\tDescription: 'Deploys from GitHub Actions (gkm deploy:github)',\n\t\t\t\t// SST deploys can run long; the default hour is tight.\n\t\t\t\tMaxSessionDuration: 3600 * 2,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t}\n\n\t// Attaching an attached policy is a no-op, so this converges too.\n\tawait iam.send(\n\t\tnew AttachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t);\n\treturn arn;\n}\n\n/**\n * An IAM client for the stage's account.\n *\n * `fromIni` for a named profile, deliberately: it resolves exactly that\n * profile — keys, assume-role, `credential_process` or SSO, whichever\n * ~/.aws/config says — and never falls back to AWS_* in the environment. The\n * provider chain checks the environment first, so exported staging keys would\n * quietly win over `--profile prod` and the production role would land in the\n * staging account.\n */\nexport async function iamFor(\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<IAMClient> {\n\tconst config: IAMClientConfig = { region: 'us-east-1' };\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\tconfig.credentials = { accessKeyId: 'test', secretAccessKey: 'test' };\n\t}\n\treturn new IAMClient(config);\n}\n\nexport async function deployGithubCommand(\n\toptions: DeployGithubOptions,\n\tdeps: { iam?: IAMClient; gh?: Gh; cwd?: string } = {},\n): Promise<{ roleArn?: string; repo: string }> {\n\tconst gh = deps.gh ?? defaultGh;\n\tconst { workspace } = await loadWorkspaceConfig(deps.cwd);\n\tassertDeployedStage(workspace.stages, options.stage);\n\n\tconst repo =\n\t\toptions.repo ??\n\t\tgh(['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner']);\n\tconst role = roleName(workspace.name, options.stage);\n\tconst policyArn = options.policyArn ?? DEFAULT_POLICY_ARN;\n\tconst keyPath = getKeyPath(options.stage, workspace.name);\n\tconst hasKey = existsSync(keyPath);\n\t// A stage whose secrets are in a store is read from there by the deploy job,\n\t// with the role; it needs no key on GitHub.\n\tconst remote = isRemoteStore(workspace, options.stage);\n\t// Read from the stage's own store, with the same credentials the role is\n\t// created with: what the deploy job will find there.\n\tconst store = await secretsStoreFor(workspace, options.stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst hasSecrets = remote && (await store.read(options.stage)) !== null;\n\tconst via = options.profile\n\t\t? `profile \"${options.profile}\"`\n\t\t: 'the default credentials';\n\n\tlogger.log(`\\n🔐 GitHub → AWS for stage \"${options.stage}\"\\n`);\n\tlogger.log(` Repository: ${repo}`);\n\tlogger.log(` AWS profile: ${options.profile ?? '(default credentials)'}`);\n\tlogger.log(` Role: ${role}`);\n\tlogger.log(\n\t\t` Policy: ${policyArn}${options.policyArn ? '' : ' (default; --policy-arn to narrow)'}`,\n\t);\n\tlogger.log(` Trusted by: repo:${repo}:environment:${options.stage} only`);\n\tif (remote) {\n\t\tlogger.log(\n\t\t\t` Secrets: ${hasSecrets ? `in the store (${store.name}), read with ${via}` : `none in the store yet — gkm secrets:set <KEY> '…' --stage ${options.stage}`}`,\n\t\t);\n\t} else {\n\t\tlogger.log(\n\t\t\t` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`,\n\t\t);\n\t}\n\n\tif (options.dryRun) {\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo };\n\t}\n\n\tconst iam = deps.iam ?? (await iamFor(options.profile));\n\tconst providerArn = await ensureOidcProvider(iam).catch((error) => {\n\t\t// The first AWS call is where an expired SSO login surfaces; the SDK's\n\t\t// message does not say which profile, and this command knows.\n\t\tif (/SSO session/i.test(String(error?.message)) && options.profile) {\n\t\t\tthrow new SsoSessionExpired(options.profile);\n\t\t}\n\t\tthrow error;\n\t});\n\tconst roleArn = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, repo, options.stage),\n\t\tpolicyArn,\n\t);\n\tlogger.log(`\\n ✓ AWS: ${roleArn}`);\n\n\t// The environment the generated deploy workflow runs the stage in.\n\tgh(['api', '--method', 'PUT', `repos/${repo}/environments/${options.stage}`]);\n\tgh([\n\t\t'variable',\n\t\t'set',\n\t\t'AWS_ROLE_ARN',\n\t\t'--env',\n\t\toptions.stage,\n\t\t'--repo',\n\t\trepo,\n\t\t'--body',\n\t\troleArn,\n\t]);\n\tlogger.log(` ✓ GitHub: environment \"${options.stage}\", AWS_ROLE_ARN`);\n\n\tif (remote) {\n\t\t// Nothing to send: the deploy job reads the stage's store itself.\n\t\tif (!hasSecrets) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ The \"${options.stage}\" store holds no secrets yet; set them with gkm secrets:set <KEY> '…' --stage ${options.stage}${options.profile ? ` (AWS_PROFILE=${options.profile})` : ''}.`,\n\t\t\t);\n\t\t}\n\t} else if (hasKey) {\n\t\t// Through stdin, so the key is never an argument a process list shows.\n\t\tgh(\n\t\t\t[\n\t\t\t\t'secret',\n\t\t\t\t'set',\n\t\t\t\t'GKM_SECRETS_KEY',\n\t\t\t\t'--env',\n\t\t\t\toptions.stage,\n\t\t\t\t'--repo',\n\t\t\t\trepo,\n\t\t\t],\n\t\t\treadFileSync(keyPath, 'utf-8').trim(),\n\t\t);\n\t\tlogger.log(' ✓ GitHub: GKM_SECRETS_KEY');\n\t} else {\n\t\tlogger.log(\n\t\t\t` ⚠ No secrets key for \"${options.stage}\"; run gkm secrets:init --stage ${options.stage}, then this again.`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t`\\n Protect it: add required reviewers to the \"${options.stage}\" environment in the repository settings.\\n`,\n\t);\n\treturn { roleArn, repo };\n}\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends Error {\n\tconstructor(readonly profile: string) {\n\t\tsuper(\n\t\t\t`The SSO session for profile \"${profile}\" has expired or is invalid. Run: aws sso login --profile ${profile}`,\n\t\t);\n\t\tthis.name = 'SsoSessionExpired';\n\t}\n}\n"],"mappings":"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"}
@@ -0,0 +1,213 @@
1
+ import { l as loadWorkspaceConfig } from "./config-5watB1D0.mjs";
2
+ import { D as assertDeployedStage } from "./workspace-DOmIhiau.mjs";
3
+ import { a as getKeyPath, n as secretsStoreFor, t as isRemoteStore } from "./store-DeGATCS-.mjs";
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ import { execFileSync } from "node:child_process";
6
+ import { AttachRolePolicyCommand, CreateOpenIDConnectProviderCommand, CreateRoleCommand, GetRoleCommand, IAMClient, ListOpenIDConnectProvidersCommand, UpdateAssumeRolePolicyCommand } from "@aws-sdk/client-iam";
7
+ //#region src/deploy/github.ts
8
+ /**
9
+ * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.
10
+ *
11
+ * A stage usually lives in its own AWS account — staging in one, production in
12
+ * another — so this runs once per stage, with that account's profile:
13
+ *
14
+ * gkm deploy:github --stage staging --profile acme-dev
15
+ * gkm deploy:github --stage prod --profile acme-prod
16
+ *
17
+ * In the stage's account it makes sure GitHub's OIDC provider exists, and a
18
+ * role that only this repository's `<stage>` environment can assume. On
19
+ * GitHub it creates that environment and gives it what the generated deploy
20
+ * workflow reads: `AWS_ROLE_ARN`, and — when the stage's secrets are kept in
21
+ * the local file — its key as `GKM_SECRETS_KEY`. With `secrets.store` set to
22
+ * SSM the deploy job pulls them with the role instead, so no key is handed to
23
+ * GitHub, and this pushes the stage's local secrets to SSM in the same account,
24
+ * with the same profile. Re-running it converges rather than duplicating.
25
+ */
26
+ const logger = console;
27
+ const GITHUB_OIDC_HOST = "token.actions.githubusercontent.com";
28
+ const AUDIENCE = "sts.amazonaws.com";
29
+ const defaultGh = (args, input) => execFileSync("gh", args, {
30
+ encoding: "utf-8",
31
+ ...input === void 0 ? {} : {
32
+ input,
33
+ stdio: [
34
+ "pipe",
35
+ "pipe",
36
+ "pipe"
37
+ ]
38
+ }
39
+ }).trim();
40
+ /** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */
41
+ function roleName(project, stage) {
42
+ return `${project}-github-${stage}`.toLowerCase().replace(/[^a-z0-9+=,.@_-]/g, "-").slice(0, 64);
43
+ }
44
+ /**
45
+ * Who may assume the role: a workflow job running in this repository's
46
+ * `<stage>` environment, and nothing else — not another branch's job, not
47
+ * another environment's. That is what keeps a staging deploy from using the
48
+ * production role.
49
+ */
50
+ function trustPolicy(providerArn, repo, stage) {
51
+ return JSON.stringify({
52
+ Version: "2012-10-17",
53
+ Statement: [{
54
+ Effect: "Allow",
55
+ Principal: { Federated: providerArn },
56
+ Action: "sts:AssumeRoleWithWebIdentity",
57
+ Condition: { StringEquals: {
58
+ [`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,
59
+ [`${GITHUB_OIDC_HOST}:sub`]: `repo:${repo}:environment:${stage}`
60
+ } }
61
+ }]
62
+ });
63
+ }
64
+ /** GitHub's OIDC provider in this account, created if it is not there. */
65
+ async function ensureOidcProvider(iam) {
66
+ const { OpenIDConnectProviderList = [] } = await iam.send(new ListOpenIDConnectProvidersCommand({}));
67
+ const existing = OpenIDConnectProviderList.find((p) => p.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`))?.Arn;
68
+ if (existing) return existing;
69
+ return (await iam.send(new CreateOpenIDConnectProviderCommand({
70
+ Url: `https://${GITHUB_OIDC_HOST}`,
71
+ ClientIDList: [AUDIENCE]
72
+ }))).OpenIDConnectProviderArn;
73
+ }
74
+ /** The deploy role, created or brought back to this trust and policy. */
75
+ async function ensureRole(iam, name, trust, policyArn) {
76
+ let arn;
77
+ try {
78
+ const { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));
79
+ await iam.send(new UpdateAssumeRolePolicyCommand({
80
+ RoleName: name,
81
+ PolicyDocument: trust
82
+ }));
83
+ arn = Role.Arn;
84
+ } catch (error) {
85
+ if (error.name !== "NoSuchEntityException") throw error;
86
+ const { Role } = await iam.send(new CreateRoleCommand({
87
+ RoleName: name,
88
+ AssumeRolePolicyDocument: trust,
89
+ Description: "Deploys from GitHub Actions (gkm deploy:github)",
90
+ MaxSessionDuration: 7200
91
+ }));
92
+ arn = Role.Arn;
93
+ }
94
+ await iam.send(new AttachRolePolicyCommand({
95
+ RoleName: name,
96
+ PolicyArn: policyArn
97
+ }));
98
+ return arn;
99
+ }
100
+ /**
101
+ * An IAM client for the stage's account.
102
+ *
103
+ * `fromIni` for a named profile, deliberately: it resolves exactly that
104
+ * profile — keys, assume-role, `credential_process` or SSO, whichever
105
+ * ~/.aws/config says — and never falls back to AWS_* in the environment. The
106
+ * provider chain checks the environment first, so exported staging keys would
107
+ * quietly win over `--profile prod` and the production role would land in the
108
+ * staging account.
109
+ */
110
+ async function iamFor(profile, endpoint) {
111
+ const config = { region: "us-east-1" };
112
+ if (profile) {
113
+ const { fromIni } = await import("@aws-sdk/credential-providers");
114
+ config.credentials = fromIni({ profile });
115
+ }
116
+ if (endpoint) {
117
+ config.endpoint = endpoint;
118
+ config.credentials = {
119
+ accessKeyId: "test",
120
+ secretAccessKey: "test"
121
+ };
122
+ }
123
+ return new IAMClient(config);
124
+ }
125
+ async function deployGithubCommand(options, deps = {}) {
126
+ const gh = deps.gh ?? defaultGh;
127
+ const { workspace } = await loadWorkspaceConfig(deps.cwd);
128
+ assertDeployedStage(workspace.stages, options.stage);
129
+ const repo = options.repo ?? gh([
130
+ "repo",
131
+ "view",
132
+ "--json",
133
+ "nameWithOwner",
134
+ "-q",
135
+ ".nameWithOwner"
136
+ ]);
137
+ const role = roleName(workspace.name, options.stage);
138
+ const policyArn = options.policyArn ?? "arn:aws:iam::aws:policy/AdministratorAccess";
139
+ const keyPath = getKeyPath(options.stage, workspace.name);
140
+ const hasKey = existsSync(keyPath);
141
+ const remote = isRemoteStore(workspace, options.stage);
142
+ const store = await secretsStoreFor(workspace, options.stage, { ...options.profile ? { profile: options.profile } : {} });
143
+ const hasSecrets = remote && await store.read(options.stage) !== null;
144
+ const via = options.profile ? `profile "${options.profile}"` : "the default credentials";
145
+ logger.log(`\n🔐 GitHub → AWS for stage "${options.stage}"\n`);
146
+ logger.log(` Repository: ${repo}`);
147
+ logger.log(` AWS profile: ${options.profile ?? "(default credentials)"}`);
148
+ logger.log(` Role: ${role}`);
149
+ logger.log(` Policy: ${policyArn}${options.policyArn ? "" : " (default; --policy-arn to narrow)"}`);
150
+ logger.log(` Trusted by: repo:${repo}:environment:${options.stage} only`);
151
+ if (remote) logger.log(` Secrets: ${hasSecrets ? `in the store (${store.name}), read with ${via}` : `none in the store yet — gkm secrets:set <KEY> '…' --stage ${options.stage}`}`);
152
+ else logger.log(` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`);
153
+ if (options.dryRun) {
154
+ logger.log("\n --dry-run: nothing changed.\n");
155
+ return { repo };
156
+ }
157
+ const iam = deps.iam ?? await iamFor(options.profile);
158
+ const roleArn = await ensureRole(iam, role, trustPolicy(await ensureOidcProvider(iam).catch((error) => {
159
+ if (/SSO session/i.test(String(error?.message)) && options.profile) throw new SsoSessionExpired(options.profile);
160
+ throw error;
161
+ }), repo, options.stage), policyArn);
162
+ logger.log(`\n ✓ AWS: ${roleArn}`);
163
+ gh([
164
+ "api",
165
+ "--method",
166
+ "PUT",
167
+ `repos/${repo}/environments/${options.stage}`
168
+ ]);
169
+ gh([
170
+ "variable",
171
+ "set",
172
+ "AWS_ROLE_ARN",
173
+ "--env",
174
+ options.stage,
175
+ "--repo",
176
+ repo,
177
+ "--body",
178
+ roleArn
179
+ ]);
180
+ logger.log(` ✓ GitHub: environment "${options.stage}", AWS_ROLE_ARN`);
181
+ if (remote) {
182
+ if (!hasSecrets) logger.log(` ⚠ The "${options.stage}" store holds no secrets yet; set them with gkm secrets:set <KEY> '…' --stage ${options.stage}${options.profile ? ` (AWS_PROFILE=${options.profile})` : ""}.`);
183
+ } else if (hasKey) {
184
+ gh([
185
+ "secret",
186
+ "set",
187
+ "GKM_SECRETS_KEY",
188
+ "--env",
189
+ options.stage,
190
+ "--repo",
191
+ repo
192
+ ], readFileSync(keyPath, "utf-8").trim());
193
+ logger.log(" ✓ GitHub: GKM_SECRETS_KEY");
194
+ } else logger.log(` ⚠ No secrets key for "${options.stage}"; run gkm secrets:init --stage ${options.stage}, then this again.`);
195
+ logger.log(`\n Protect it: add required reviewers to the "${options.stage}" environment in the repository settings.\n`);
196
+ return {
197
+ roleArn,
198
+ repo
199
+ };
200
+ }
201
+ /** The profile's SSO login has lapsed; the fix is one command away. */
202
+ var SsoSessionExpired = class extends Error {
203
+ profile;
204
+ constructor(profile) {
205
+ super(`The SSO session for profile "${profile}" has expired or is invalid. Run: aws sso login --profile ${profile}`);
206
+ this.profile = profile;
207
+ this.name = "SsoSessionExpired";
208
+ }
209
+ };
210
+ //#endregion
211
+ export { deployGithubCommand };
212
+
213
+ //# sourceMappingURL=github-D5MthUlR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-D5MthUlR.mjs","names":[],"sources":["../src/deploy/github.ts"],"sourcesContent":["/**\n * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.\n *\n * A stage usually lives in its own AWS account — staging in one, production in\n * another — so this runs once per stage, with that account's profile:\n *\n * gkm deploy:github --stage staging --profile acme-dev\n * gkm deploy:github --stage prod --profile acme-prod\n *\n * In the stage's account it makes sure GitHub's OIDC provider exists, and a\n * role that only this repository's `<stage>` environment can assume. On\n * GitHub it creates that environment and gives it what the generated deploy\n * workflow reads: `AWS_ROLE_ARN`, and — when the stage's secrets are kept in\n * the local file — its key as `GKM_SECRETS_KEY`. With `secrets.store` set to\n * SSM the deploy job pulls them with the role instead, so no key is handed to\n * GitHub, and this pushes the stage's local secrets to SSM in the same account,\n * with the same profile. Re-running it converges rather than duplicating.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListOpenIDConnectProvidersCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { getKeyPath } from '../secrets/keystore.js';\nimport { isRemoteStore, secretsStoreFor } from '../secrets/store.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default —\n * said out loud in the output, and replaceable with `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\nexport interface DeployGithubOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account. */\n\tprofile?: string;\n\t/** `owner/name`; defaults to the repository `gh` sees here. */\n\trepo?: string;\n\t/** The policy the deploy role gets. */\n\tpolicyArn?: string;\n\tdryRun?: boolean;\n}\n\n/** Runs `gh` with the given arguments and optional stdin, returning stdout. */\nexport type Gh = (args: string[], input?: string) => string;\n\nconst defaultGh: Gh = (args, input) =>\n\texecFileSync('gh', args, {\n\t\tencoding: 'utf-8',\n\t\t...(input === undefined\n\t\t\t? {}\n\t\t\t: { input, stdio: ['pipe', 'pipe', 'pipe'] as const }),\n\t}).trim();\n\n/** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */\nexport function roleName(project: string, stage: string): string {\n\treturn `${project}-github-${stage}`\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9+=,.@_-]/g, '-')\n\t\t.slice(0, 64);\n}\n\n/**\n * Who may assume the role: a workflow job running in this repository's\n * `<stage>` environment, and nothing else — not another branch's job, not\n * another environment's. That is what keeps a staging deploy from using the\n * production role.\n */\nexport function trustPolicy(\n\tproviderArn: string,\n\trepo: string,\n\tstage: string,\n): string {\n\treturn JSON.stringify({\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tPrincipal: { Federated: providerArn },\n\t\t\t\tAction: 'sts:AssumeRoleWithWebIdentity',\n\t\t\t\tCondition: {\n\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:sub`]: `repo:${repo}:environment:${stage}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/** GitHub's OIDC provider in this account, created if it is not there. */\nexport async function ensureOidcProvider(iam: IAMClient): Promise<string> {\n\tconst { OpenIDConnectProviderList = [] } = await iam.send(\n\t\tnew ListOpenIDConnectProvidersCommand({}),\n\t);\n\tconst existing = OpenIDConnectProviderList.find((p) =>\n\t\tp.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`),\n\t)?.Arn;\n\tif (existing) return existing;\n\n\tconst created = await iam.send(\n\t\tnew CreateOpenIDConnectProviderCommand({\n\t\t\tUrl: `https://${GITHUB_OIDC_HOST}`,\n\t\t\tClientIDList: [AUDIENCE],\n\t\t}),\n\t);\n\treturn created.OpenIDConnectProviderArn!;\n}\n\n/** The deploy role, created or brought back to this trust and policy. */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\tpolicyArn: string,\n): Promise<string> {\n\tlet arn: string;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tawait iam.send(\n\t\t\tnew UpdateAssumeRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyDocument: trust,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name !== 'NoSuchEntityException') {\n\t\t\tthrow error;\n\t\t}\n\t\tconst { Role } = await iam.send(\n\t\t\tnew CreateRoleCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tAssumeRolePolicyDocument: trust,\n\t\t\t\tDescription: 'Deploys from GitHub Actions (gkm deploy:github)',\n\t\t\t\t// SST deploys can run long; the default hour is tight.\n\t\t\t\tMaxSessionDuration: 3600 * 2,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t}\n\n\t// Attaching an attached policy is a no-op, so this converges too.\n\tawait iam.send(\n\t\tnew AttachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t);\n\treturn arn;\n}\n\n/**\n * An IAM client for the stage's account.\n *\n * `fromIni` for a named profile, deliberately: it resolves exactly that\n * profile — keys, assume-role, `credential_process` or SSO, whichever\n * ~/.aws/config says — and never falls back to AWS_* in the environment. The\n * provider chain checks the environment first, so exported staging keys would\n * quietly win over `--profile prod` and the production role would land in the\n * staging account.\n */\nexport async function iamFor(\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<IAMClient> {\n\tconst config: IAMClientConfig = { region: 'us-east-1' };\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\tconfig.credentials = { accessKeyId: 'test', secretAccessKey: 'test' };\n\t}\n\treturn new IAMClient(config);\n}\n\nexport async function deployGithubCommand(\n\toptions: DeployGithubOptions,\n\tdeps: { iam?: IAMClient; gh?: Gh; cwd?: string } = {},\n): Promise<{ roleArn?: string; repo: string }> {\n\tconst gh = deps.gh ?? defaultGh;\n\tconst { workspace } = await loadWorkspaceConfig(deps.cwd);\n\tassertDeployedStage(workspace.stages, options.stage);\n\n\tconst repo =\n\t\toptions.repo ??\n\t\tgh(['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner']);\n\tconst role = roleName(workspace.name, options.stage);\n\tconst policyArn = options.policyArn ?? DEFAULT_POLICY_ARN;\n\tconst keyPath = getKeyPath(options.stage, workspace.name);\n\tconst hasKey = existsSync(keyPath);\n\t// A stage whose secrets are in a store is read from there by the deploy job,\n\t// with the role; it needs no key on GitHub.\n\tconst remote = isRemoteStore(workspace, options.stage);\n\t// Read from the stage's own store, with the same credentials the role is\n\t// created with: what the deploy job will find there.\n\tconst store = await secretsStoreFor(workspace, options.stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst hasSecrets = remote && (await store.read(options.stage)) !== null;\n\tconst via = options.profile\n\t\t? `profile \"${options.profile}\"`\n\t\t: 'the default credentials';\n\n\tlogger.log(`\\n🔐 GitHub → AWS for stage \"${options.stage}\"\\n`);\n\tlogger.log(` Repository: ${repo}`);\n\tlogger.log(` AWS profile: ${options.profile ?? '(default credentials)'}`);\n\tlogger.log(` Role: ${role}`);\n\tlogger.log(\n\t\t` Policy: ${policyArn}${options.policyArn ? '' : ' (default; --policy-arn to narrow)'}`,\n\t);\n\tlogger.log(` Trusted by: repo:${repo}:environment:${options.stage} only`);\n\tif (remote) {\n\t\tlogger.log(\n\t\t\t` Secrets: ${hasSecrets ? `in the store (${store.name}), read with ${via}` : `none in the store yet — gkm secrets:set <KEY> '…' --stage ${options.stage}`}`,\n\t\t);\n\t} else {\n\t\tlogger.log(\n\t\t\t` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`,\n\t\t);\n\t}\n\n\tif (options.dryRun) {\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo };\n\t}\n\n\tconst iam = deps.iam ?? (await iamFor(options.profile));\n\tconst providerArn = await ensureOidcProvider(iam).catch((error) => {\n\t\t// The first AWS call is where an expired SSO login surfaces; the SDK's\n\t\t// message does not say which profile, and this command knows.\n\t\tif (/SSO session/i.test(String(error?.message)) && options.profile) {\n\t\t\tthrow new SsoSessionExpired(options.profile);\n\t\t}\n\t\tthrow error;\n\t});\n\tconst roleArn = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, repo, options.stage),\n\t\tpolicyArn,\n\t);\n\tlogger.log(`\\n ✓ AWS: ${roleArn}`);\n\n\t// The environment the generated deploy workflow runs the stage in.\n\tgh(['api', '--method', 'PUT', `repos/${repo}/environments/${options.stage}`]);\n\tgh([\n\t\t'variable',\n\t\t'set',\n\t\t'AWS_ROLE_ARN',\n\t\t'--env',\n\t\toptions.stage,\n\t\t'--repo',\n\t\trepo,\n\t\t'--body',\n\t\troleArn,\n\t]);\n\tlogger.log(` ✓ GitHub: environment \"${options.stage}\", AWS_ROLE_ARN`);\n\n\tif (remote) {\n\t\t// Nothing to send: the deploy job reads the stage's store itself.\n\t\tif (!hasSecrets) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ The \"${options.stage}\" store holds no secrets yet; set them with gkm secrets:set <KEY> '…' --stage ${options.stage}${options.profile ? ` (AWS_PROFILE=${options.profile})` : ''}.`,\n\t\t\t);\n\t\t}\n\t} else if (hasKey) {\n\t\t// Through stdin, so the key is never an argument a process list shows.\n\t\tgh(\n\t\t\t[\n\t\t\t\t'secret',\n\t\t\t\t'set',\n\t\t\t\t'GKM_SECRETS_KEY',\n\t\t\t\t'--env',\n\t\t\t\toptions.stage,\n\t\t\t\t'--repo',\n\t\t\t\trepo,\n\t\t\t],\n\t\t\treadFileSync(keyPath, 'utf-8').trim(),\n\t\t);\n\t\tlogger.log(' ✓ GitHub: GKM_SECRETS_KEY');\n\t} else {\n\t\tlogger.log(\n\t\t\t` ⚠ No secrets key for \"${options.stage}\"; run gkm secrets:init --stage ${options.stage}, then this again.`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t`\\n Protect it: add required reviewers to the \"${options.stage}\" environment in the repository settings.\\n`,\n\t);\n\treturn { roleArn, repo };\n}\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends Error {\n\tconstructor(readonly profile: string) {\n\t\tsuper(\n\t\t\t`The SSO session for profile \"${profile}\" has expired or is invalid. Run: aws sso login --profile ${profile}`,\n\t\t);\n\t\tthis.name = 'SsoSessionExpired';\n\t}\n}\n"],"mappings":"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"}
@@ -1,7 +1,5 @@
1
- import { AppsRecord, Framework, GkmConfig, InferredWorkspaceConfig, LoadedConfig, NormalizedWorkspace, Routes, WorkspaceConfig, WorkspaceInput } from "./types-BhIl-COG.cjs";
2
-
1
+ import { C as WorkspaceInput, E as Framework, S as WorkspaceConfig, a as Routes, g as LoadedConfig, h as InferredWorkspaceConfig, l as AppsRecord, r as GkmConfig, y as NormalizedWorkspace } from "./types-DfdYe31u.cjs";
3
2
  //#region src/workspace/publicEnv.d.ts
4
-
5
3
  /**
6
4
  * All public env-var prefixes the toolbox understands.
7
5
  *
@@ -9,7 +7,7 @@ import { AppsRecord, Framework, GkmConfig, InferredWorkspaceConfig, LoadedConfig
9
7
  * (e.g. `VITE_AUTH_URL` → `auth`). A var matching any of these
10
8
  * is considered safe to inline into a client bundle.
11
9
  */
12
- declare const PUBLIC_ENV_PREFIXES: readonly ["NEXT_PUBLIC_", "VITE_", "EXPO_PUBLIC_"];
10
+ declare const PUBLIC_ENV_PREFIXES: readonly ['NEXT_PUBLIC_', 'VITE_', 'EXPO_PUBLIC_'];
13
11
  /**
14
12
  * Resolve the public env-var prefix that a frontend framework's bundler
15
13
  * inlines into client code at build time:
@@ -30,8 +28,6 @@ declare function getPublicEnvPrefix(framework?: Framework): string;
30
28
  * Returns the un-prefixed name, or `null` if no prefix matched.
31
29
  */
32
30
  declare function stripPublicPrefix(name: string): string | null;
33
- //# sourceMappingURL=publicEnv.d.ts.map
34
-
35
31
  //#endregion
36
32
  //#region src/workspace/index.d.ts
37
33
  /**
@@ -66,10 +62,6 @@ declare function stripPublicPrefix(name: string): string | null;
66
62
  * dependencies: ['api'], // <- autocomplete shows 'api' | 'web'
67
63
  * },
68
64
  * },
69
- * services: {
70
- * db: true,
71
- * cache: true,
72
- * },
73
65
  * });
74
66
  *
75
67
  * // config.apps.api <- full type inference
@@ -129,16 +121,6 @@ declare function getAppGkmConfig(workspace: NormalizedWorkspace, appName: string
129
121
  * Apps with no dependencies come first, then apps that depend on them.
130
122
  */
131
123
  declare function getAppBuildOrder(workspace: NormalizedWorkspace): string[];
132
- /**
133
- * Generate environment variables for app dependencies.
134
- *
135
- * Each dependency gets the un-prefixed `{DEP}_URL` (for server-side use) and,
136
- * when the consuming app's framework supports a public-var prefix, also the
137
- * prefixed form (e.g. `NEXT_PUBLIC_{DEP}_URL` for Next.js, `VITE_{DEP}_URL`
138
- * for Vite/TanStack Start). The prefixed form is what gets bundled into the
139
- * client at build time.
140
- */
141
- declare function getDependencyEnvVars(workspace: NormalizedWorkspace, appName: string, urlPrefix?: string): Record<string, string>;
142
124
  /**
143
125
  * Resolve the Dokploy endpoint for a specific stage.
144
126
  *
@@ -169,7 +151,6 @@ declare function getEndpointForStage(dokployConfig: {
169
151
  endpoint?: string;
170
152
  endpoints?: Record<string, string>;
171
153
  } | undefined, stage: string): string | undefined;
172
- //# sourceMappingURL=index.d.ts.map
173
154
  //#endregion
174
- export { PUBLIC_ENV_PREFIXES, allConstructGlobs, appConstructGlobs, constructPatterns, defineWorkspace, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getEndpointForStage, getPublicEnvPrefix, normalizeWorkspace, processConfig, stripPublicPrefix, workspaceConstructGlobs, wrapSingleAppAsWorkspace };
175
- //# sourceMappingURL=index-DCtipn2h.d.cts.map
155
+ export { getAppBuildOrder as a, normalizeWorkspace as c, wrapSingleAppAsWorkspace as d, PUBLIC_ENV_PREFIXES as f, defineWorkspace as i, processConfig as l, stripPublicPrefix as m, appConstructGlobs as n, getAppGkmConfig as o, getPublicEnvPrefix as p, constructPatterns as r, getEndpointForStage as s, allConstructGlobs as t, workspaceConstructGlobs as u };
156
+ //# sourceMappingURL=index-B3PeGOQp.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-B3PeGOQp.d.cts","names":[],"sources":["../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"mappings":";;;;;;;;;cASa;;;;;;;;;;;;;;;iBAoBG,mBAAmB,YAAY;;;;;iBAoB/B,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC2ElB,sBAAsB,cAAc,YACnD,QAAQ,eAAe,SACrB,wBAAwB;;;;iBA8BX,mBACf,QAAQ,iBACR,cACE;;;;;iBA+Ca,yBACf,QAAQ,WACR,cACE;;;;;iBA2Da,cACf,QAAQ,YAAY,iBACpB,cACE;;;;;;;iBA6Ba,wBACf,WAAW;;;;;;;;;;;;iBAkBI,kBACf,WAAW,qBACX;;;;;;;iBAsBe,kBAAkB,WAAW;;iBAa7B,kBAAkB,QAAQ;;;;;iBAiB1B,gBACf,WAAW,qBACX,kBACE;;;;;iBAkCa,iBAAiB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoD5B,oBACf;EACK;EAAmB,YAAY;eAEpC"}
@@ -1,7 +1,5 @@
1
- import { AppsRecord, Framework, GkmConfig, InferredWorkspaceConfig, LoadedConfig, NormalizedWorkspace, Routes, WorkspaceConfig, WorkspaceInput } from "./types-C-arnQ1r.mjs";
2
-
1
+ import { C as WorkspaceInput, E as Framework, S as WorkspaceConfig, a as Routes, g as LoadedConfig, h as InferredWorkspaceConfig, l as AppsRecord, r as GkmConfig, y as NormalizedWorkspace } from "./types-0R124hrY.mjs";
3
2
  //#region src/workspace/publicEnv.d.ts
4
-
5
3
  /**
6
4
  * All public env-var prefixes the toolbox understands.
7
5
  *
@@ -9,7 +7,7 @@ import { AppsRecord, Framework, GkmConfig, InferredWorkspaceConfig, LoadedConfig
9
7
  * (e.g. `VITE_AUTH_URL` → `auth`). A var matching any of these
10
8
  * is considered safe to inline into a client bundle.
11
9
  */
12
- declare const PUBLIC_ENV_PREFIXES: readonly ["NEXT_PUBLIC_", "VITE_", "EXPO_PUBLIC_"];
10
+ declare const PUBLIC_ENV_PREFIXES: readonly ['NEXT_PUBLIC_', 'VITE_', 'EXPO_PUBLIC_'];
13
11
  /**
14
12
  * Resolve the public env-var prefix that a frontend framework's bundler
15
13
  * inlines into client code at build time:
@@ -30,8 +28,6 @@ declare function getPublicEnvPrefix(framework?: Framework): string;
30
28
  * Returns the un-prefixed name, or `null` if no prefix matched.
31
29
  */
32
30
  declare function stripPublicPrefix(name: string): string | null;
33
- //# sourceMappingURL=publicEnv.d.ts.map
34
-
35
31
  //#endregion
36
32
  //#region src/workspace/index.d.ts
37
33
  /**
@@ -66,10 +62,6 @@ declare function stripPublicPrefix(name: string): string | null;
66
62
  * dependencies: ['api'], // <- autocomplete shows 'api' | 'web'
67
63
  * },
68
64
  * },
69
- * services: {
70
- * db: true,
71
- * cache: true,
72
- * },
73
65
  * });
74
66
  *
75
67
  * // config.apps.api <- full type inference
@@ -129,16 +121,6 @@ declare function getAppGkmConfig(workspace: NormalizedWorkspace, appName: string
129
121
  * Apps with no dependencies come first, then apps that depend on them.
130
122
  */
131
123
  declare function getAppBuildOrder(workspace: NormalizedWorkspace): string[];
132
- /**
133
- * Generate environment variables for app dependencies.
134
- *
135
- * Each dependency gets the un-prefixed `{DEP}_URL` (for server-side use) and,
136
- * when the consuming app's framework supports a public-var prefix, also the
137
- * prefixed form (e.g. `NEXT_PUBLIC_{DEP}_URL` for Next.js, `VITE_{DEP}_URL`
138
- * for Vite/TanStack Start). The prefixed form is what gets bundled into the
139
- * client at build time.
140
- */
141
- declare function getDependencyEnvVars(workspace: NormalizedWorkspace, appName: string, urlPrefix?: string): Record<string, string>;
142
124
  /**
143
125
  * Resolve the Dokploy endpoint for a specific stage.
144
126
  *
@@ -169,7 +151,6 @@ declare function getEndpointForStage(dokployConfig: {
169
151
  endpoint?: string;
170
152
  endpoints?: Record<string, string>;
171
153
  } | undefined, stage: string): string | undefined;
172
- //# sourceMappingURL=index.d.ts.map
173
154
  //#endregion
174
- export { PUBLIC_ENV_PREFIXES, allConstructGlobs, appConstructGlobs, constructPatterns, defineWorkspace, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getEndpointForStage, getPublicEnvPrefix, normalizeWorkspace, processConfig, stripPublicPrefix, workspaceConstructGlobs, wrapSingleAppAsWorkspace };
175
- //# sourceMappingURL=index-BThUzEIp.d.mts.map
155
+ export { getAppBuildOrder as a, normalizeWorkspace as c, wrapSingleAppAsWorkspace as d, PUBLIC_ENV_PREFIXES as f, defineWorkspace as i, processConfig as l, stripPublicPrefix as m, appConstructGlobs as n, getAppGkmConfig as o, getPublicEnvPrefix as p, constructPatterns as r, getEndpointForStage as s, allConstructGlobs as t, workspaceConstructGlobs as u };
156
+ //# sourceMappingURL=index-BOyaGcCm.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-BOyaGcCm.d.mts","names":[],"sources":["../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"mappings":";;;;;;;;;cASa;;;;;;;;;;;;;;;iBAoBG,mBAAmB,YAAY;;;;;iBAoB/B,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC2ElB,sBAAsB,cAAc,YACnD,QAAQ,eAAe,SACrB,wBAAwB;;;;iBA8BX,mBACf,QAAQ,iBACR,cACE;;;;;iBA+Ca,yBACf,QAAQ,WACR,cACE;;;;;iBA2Da,cACf,QAAQ,YAAY,iBACpB,cACE;;;;;;;iBA6Ba,wBACf,WAAW;;;;;;;;;;;;iBAkBI,kBACf,WAAW,qBACX;;;;;;;iBAsBe,kBAAkB,WAAW;;iBAa7B,kBAAkB,QAAQ;;;;;iBAiB1B,gBACf,WAAW,qBACX,kBACE;;;;;iBAkCa,iBAAiB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoD5B,oBACf;EACK;EAAmB,YAAY;eAEpC"}