@intentius/chant-lexicon-terraform 0.124.52 → 0.124.55

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 (641) hide show
  1. package/README.md +14 -10
  2. package/dist/__fixtures__/pr-loop/estate.component.d.ts +7 -0
  3. package/dist/__fixtures__/pr-loop/estate.component.d.ts.map +1 -0
  4. package/dist/__fixtures__/pr-loop/estate.component.js +7 -0
  5. package/dist/__fixtures__/report/generate.d.ts +26 -0
  6. package/dist/__fixtures__/report/generate.d.ts.map +1 -0
  7. package/dist/__fixtures__/report/generate.js +58 -0
  8. package/dist/applied-state.d.ts +27 -0
  9. package/dist/applied-state.d.ts.map +1 -0
  10. package/dist/applied-state.js +44 -0
  11. package/dist/backend-config.d.ts +53 -0
  12. package/dist/backend-config.d.ts.map +1 -0
  13. package/dist/backend-config.js +136 -0
  14. package/dist/behaviour/kinds.d.ts.map +1 -1
  15. package/dist/behaviour/kinds.js +4 -0
  16. package/dist/behaviour/predict.d.ts.map +1 -1
  17. package/dist/behaviour/predict.js +6 -2
  18. package/dist/behaviour/reference-catalog.d.ts +16 -5
  19. package/dist/behaviour/reference-catalog.d.ts.map +1 -1
  20. package/dist/behaviour/reference-catalog.js +16 -5
  21. package/dist/behaviour/request.d.ts.map +1 -1
  22. package/dist/behaviour/request.js +3 -2
  23. package/dist/changed-roots.d.ts +6 -4
  24. package/dist/changed-roots.d.ts.map +1 -1
  25. package/dist/changed-roots.js +14 -18
  26. package/dist/ci-setup.d.ts +11 -0
  27. package/dist/ci-setup.d.ts.map +1 -0
  28. package/dist/ci-setup.js +41 -0
  29. package/dist/commands.d.ts.map +1 -1
  30. package/dist/commands.js +4 -3
  31. package/dist/components/capability-plugin.d.ts +105 -9
  32. package/dist/components/capability-plugin.d.ts.map +1 -1
  33. package/dist/components/capability-plugin.js +357 -36
  34. package/dist/components/compose-group.d.ts +145 -0
  35. package/dist/components/compose-group.d.ts.map +1 -0
  36. package/dist/components/compose-group.js +407 -0
  37. package/dist/components/placeholder-preview.d.ts +78 -0
  38. package/dist/components/placeholder-preview.d.ts.map +1 -0
  39. package/dist/components/placeholder-preview.js +254 -0
  40. package/dist/components/release-inputs.d.ts +29 -0
  41. package/dist/components/release-inputs.d.ts.map +1 -0
  42. package/dist/components/release-inputs.js +57 -0
  43. package/dist/components/trace-facts.d.ts +16 -0
  44. package/dist/components/trace-facts.d.ts.map +1 -0
  45. package/dist/components/trace-facts.js +19 -0
  46. package/dist/compose/apply-events.d.ts +46 -0
  47. package/dist/compose/apply-events.d.ts.map +1 -0
  48. package/dist/compose/apply-events.js +77 -0
  49. package/dist/compose/estate.d.ts +194 -0
  50. package/dist/compose/estate.d.ts.map +1 -0
  51. package/dist/compose/estate.js +363 -0
  52. package/dist/compose/hcl.d.ts +99 -0
  53. package/dist/compose/hcl.d.ts.map +1 -0
  54. package/dist/compose/hcl.js +302 -0
  55. package/dist/compose/module-address.d.ts +24 -0
  56. package/dist/compose/module-address.d.ts.map +1 -0
  57. package/dist/compose/module-address.js +43 -0
  58. package/dist/compose/root.d.ts +127 -0
  59. package/dist/compose/root.d.ts.map +1 -0
  60. package/dist/compose/root.js +586 -0
  61. package/dist/compose/run.d.ts +183 -0
  62. package/dist/compose/run.d.ts.map +1 -0
  63. package/dist/compose/run.js +447 -0
  64. package/dist/compose/slice.d.ts +102 -0
  65. package/dist/compose/slice.d.ts.map +1 -0
  66. package/dist/compose/slice.js +249 -0
  67. package/dist/compose/state-address.d.ts +28 -0
  68. package/dist/compose/state-address.d.ts.map +1 -0
  69. package/dist/compose/state-address.js +154 -0
  70. package/dist/compose/state-merge.d.ts +52 -0
  71. package/dist/compose/state-merge.d.ts.map +1 -0
  72. package/dist/compose/state-merge.js +135 -0
  73. package/dist/compose.d.ts +179 -0
  74. package/dist/compose.d.ts.map +1 -0
  75. package/dist/compose.js +482 -0
  76. package/dist/composites/terraform-apply-op.d.ts.map +1 -1
  77. package/dist/composites/terraform-apply-op.js +7 -3
  78. package/dist/cone.d.ts +53 -0
  79. package/dist/cone.d.ts.map +1 -0
  80. package/dist/cone.js +119 -0
  81. package/dist/config.d.ts +59 -13
  82. package/dist/config.d.ts.map +1 -1
  83. package/dist/config.js +43 -9
  84. package/dist/cost-hold.d.ts +27 -0
  85. package/dist/cost-hold.d.ts.map +1 -0
  86. package/dist/cost-hold.js +21 -0
  87. package/dist/describe-resources.d.ts +74 -12
  88. package/dist/describe-resources.d.ts.map +1 -1
  89. package/dist/describe-resources.js +374 -66
  90. package/dist/drift-read.d.ts +33 -0
  91. package/dist/drift-read.d.ts.map +1 -0
  92. package/dist/drift-read.js +243 -0
  93. package/dist/drift-record.d.ts +147 -0
  94. package/dist/drift-record.d.ts.map +1 -0
  95. package/dist/drift-record.js +383 -0
  96. package/dist/drift-rows.d.ts +61 -0
  97. package/dist/drift-rows.d.ts.map +1 -0
  98. package/dist/drift-rows.js +108 -0
  99. package/dist/exit-codes.d.ts +55 -0
  100. package/dist/exit-codes.d.ts.map +1 -0
  101. package/dist/exit-codes.js +48 -0
  102. package/dist/hcl/edges.d.ts.map +1 -1
  103. package/dist/hcl/edges.js +24 -1
  104. package/dist/hcl/files.d.ts +61 -0
  105. package/dist/hcl/files.d.ts.map +1 -0
  106. package/dist/hcl/files.js +221 -0
  107. package/dist/hcl/instances.d.ts +82 -0
  108. package/dist/hcl/instances.d.ts.map +1 -0
  109. package/dist/hcl/instances.js +390 -0
  110. package/dist/hcl/parse.d.ts +81 -9
  111. package/dist/hcl/parse.d.ts.map +1 -1
  112. package/dist/hcl/parse.js +114 -16
  113. package/dist/hcl/roots.d.ts.map +1 -1
  114. package/dist/hcl/roots.js +5 -0
  115. package/dist/hcl/suppressions.d.ts.map +1 -1
  116. package/dist/hcl/suppressions.js +8 -2
  117. package/dist/index.d.ts +13 -2
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +24 -2
  120. package/dist/integrity.json +8 -5
  121. package/dist/lint/audit-catalog.d.ts.map +1 -1
  122. package/dist/lint/audit-catalog.js +27 -3
  123. package/dist/lint/audit-lineage.d.ts +14 -2
  124. package/dist/lint/audit-lineage.d.ts.map +1 -1
  125. package/dist/lint/post-synth/index.d.ts.map +1 -1
  126. package/dist/lint/post-synth/index.js +4 -0
  127. package/dist/lint/post-synth/record-store.d.ts +54 -0
  128. package/dist/lint/post-synth/record-store.d.ts.map +1 -0
  129. package/dist/lint/post-synth/record-store.js +104 -0
  130. package/dist/lint/post-synth/tf001.d.ts +2 -2
  131. package/dist/lint/post-synth/tf001.js +2 -2
  132. package/dist/lint/post-synth/tf024.d.ts +35 -11
  133. package/dist/lint/post-synth/tf024.d.ts.map +1 -1
  134. package/dist/lint/post-synth/tf024.js +92 -20
  135. package/dist/lint/post-synth/tf046.d.ts +64 -0
  136. package/dist/lint/post-synth/tf046.d.ts.map +1 -0
  137. package/dist/lint/post-synth/tf046.js +175 -0
  138. package/dist/lint/post-synth/tf047.d.ts +36 -0
  139. package/dist/lint/post-synth/tf047.d.ts.map +1 -0
  140. package/dist/lint/post-synth/tf047.js +94 -0
  141. package/dist/live-plan-reasons.d.ts +21 -0
  142. package/dist/live-plan-reasons.d.ts.map +1 -0
  143. package/dist/live-plan-reasons.js +47 -0
  144. package/dist/lock-rows.d.ts +36 -0
  145. package/dist/lock-rows.d.ts.map +1 -0
  146. package/dist/lock-rows.js +60 -0
  147. package/dist/lock-timeout.d.ts +22 -0
  148. package/dist/lock-timeout.d.ts.map +1 -0
  149. package/dist/lock-timeout.js +32 -0
  150. package/dist/lsp/option-keys.d.ts.map +1 -1
  151. package/dist/lsp/option-keys.js +2 -0
  152. package/dist/manifest.json +1 -1
  153. package/dist/module-catalog/index.d.ts +69 -0
  154. package/dist/module-catalog/index.d.ts.map +1 -0
  155. package/dist/module-catalog/index.js +110 -0
  156. package/dist/module-catalog/refresh-cli.d.ts +4 -0
  157. package/dist/module-catalog/refresh-cli.d.ts.map +1 -0
  158. package/dist/module-catalog/refresh-cli.js +64 -0
  159. package/dist/module-catalog/static-tier.d.ts +6 -0
  160. package/dist/module-catalog/static-tier.d.ts.map +1 -0
  161. package/dist/module-catalog/static-tier.js +1650 -0
  162. package/dist/module-catalog/types.d.ts +65 -0
  163. package/dist/module-catalog/types.d.ts.map +1 -0
  164. package/dist/module-catalog/types.js +33 -0
  165. package/dist/okf/index.md +3 -1
  166. package/dist/okf/rules/TF024.md +2 -2
  167. package/dist/okf/rules/TF046.md +11 -0
  168. package/dist/okf/rules/TF047.md +11 -0
  169. package/dist/op/activities/index.d.ts +6 -2
  170. package/dist/op/activities/index.d.ts.map +1 -1
  171. package/dist/op/activities/index.js +5 -1
  172. package/dist/op/activities/live-detect.d.ts +4 -3
  173. package/dist/op/activities/live-detect.d.ts.map +1 -1
  174. package/dist/op/activities/live-detect.js +57 -32
  175. package/dist/op/activities/pin-rollout.d.ts +2 -0
  176. package/dist/op/activities/pin-rollout.d.ts.map +1 -1
  177. package/dist/op/activities/pin-rollout.js +1 -0
  178. package/dist/op/activities/terraform.d.ts +182 -5
  179. package/dist/op/activities/terraform.d.ts.map +1 -1
  180. package/dist/op/activities/terraform.js +224 -24
  181. package/dist/op/activity-contracts.d.ts +27 -0
  182. package/dist/op/activity-contracts.d.ts.map +1 -1
  183. package/dist/op/activity-contracts.js +19 -1
  184. package/dist/op/builders.d.ts +7 -1
  185. package/dist/op/builders.d.ts.map +1 -1
  186. package/dist/op/builders.js +9 -0
  187. package/dist/pin/consumers.d.ts +35 -0
  188. package/dist/pin/consumers.d.ts.map +1 -0
  189. package/dist/pin/consumers.js +70 -0
  190. package/dist/pin/edit.d.ts.map +1 -1
  191. package/dist/pin/edit.js +35 -0
  192. package/dist/pin/rollout.d.ts +2 -0
  193. package/dist/pin/rollout.d.ts.map +1 -1
  194. package/dist/pin/rollout.js +14 -11
  195. package/dist/plan-shape.d.ts +30 -0
  196. package/dist/plan-shape.d.ts.map +1 -0
  197. package/dist/plan-shape.js +125 -0
  198. package/dist/plugin.d.ts.map +1 -1
  199. package/dist/plugin.js +37 -18
  200. package/dist/registry/address.d.ts +63 -0
  201. package/dist/registry/address.d.ts.map +1 -0
  202. package/dist/registry/address.js +65 -0
  203. package/dist/registry/catalog-read.d.ts +14 -0
  204. package/dist/registry/catalog-read.d.ts.map +1 -0
  205. package/dist/registry/catalog-read.js +58 -0
  206. package/dist/registry/deprecated.d.ts +37 -0
  207. package/dist/registry/deprecated.d.ts.map +1 -0
  208. package/dist/registry/deprecated.js +43 -0
  209. package/dist/registry/docs.d.ts +96 -0
  210. package/dist/registry/docs.d.ts.map +1 -0
  211. package/dist/registry/docs.js +210 -0
  212. package/dist/registry/edge/edge.d.ts +46 -0
  213. package/dist/registry/edge/edge.d.ts.map +1 -0
  214. package/dist/registry/edge/edge.js +194 -0
  215. package/dist/registry/edge/index.d.ts +11 -0
  216. package/dist/registry/edge/index.d.ts.map +1 -0
  217. package/dist/registry/edge/index.js +10 -0
  218. package/dist/registry/edge/lambda-edge.d.ts +76 -0
  219. package/dist/registry/edge/lambda-edge.d.ts.map +1 -0
  220. package/dist/registry/edge/lambda-edge.js +95 -0
  221. package/dist/registry/edge/sigv4-web.d.ts +33 -0
  222. package/dist/registry/edge/sigv4-web.d.ts.map +1 -0
  223. package/dist/registry/edge/sigv4-web.js +56 -0
  224. package/dist/registry/edge/token-source.d.ts +73 -0
  225. package/dist/registry/edge/token-source.d.ts.map +1 -0
  226. package/dist/registry/edge/token-source.js +115 -0
  227. package/dist/registry/edge/worker.d.ts +45 -0
  228. package/dist/registry/edge/worker.d.ts.map +1 -0
  229. package/dist/registry/edge/worker.js +26 -0
  230. package/dist/registry/handler.d.ts +25 -0
  231. package/dist/registry/handler.d.ts.map +1 -0
  232. package/dist/registry/handler.js +75 -0
  233. package/dist/registry/headers-file.d.ts +21 -0
  234. package/dist/registry/headers-file.d.ts.map +1 -0
  235. package/dist/registry/headers-file.js +57 -0
  236. package/dist/registry/index.d.ts +31 -0
  237. package/dist/registry/index.d.ts.map +1 -0
  238. package/dist/registry/index.js +30 -0
  239. package/dist/registry/mirror-paths.d.ts +9 -0
  240. package/dist/registry/mirror-paths.d.ts.map +1 -0
  241. package/dist/registry/mirror-paths.js +8 -0
  242. package/dist/registry/mirror.d.ts +130 -0
  243. package/dist/registry/mirror.d.ts.map +1 -0
  244. package/dist/registry/mirror.js +319 -0
  245. package/dist/registry/proxies.d.ts +73 -0
  246. package/dist/registry/proxies.d.ts.map +1 -0
  247. package/dist/registry/proxies.js +111 -0
  248. package/dist/registry/reader.d.ts +57 -0
  249. package/dist/registry/reader.d.ts.map +1 -0
  250. package/dist/registry/reader.js +198 -0
  251. package/dist/registry/semver.d.ts +16 -0
  252. package/dist/registry/semver.d.ts.map +1 -0
  253. package/dist/registry/semver.js +24 -0
  254. package/dist/registry/serve.d.ts +24 -0
  255. package/dist/registry/serve.d.ts.map +1 -0
  256. package/dist/registry/serve.js +64 -0
  257. package/dist/registry/source-forms.d.ts +79 -0
  258. package/dist/registry/source-forms.d.ts.map +1 -0
  259. package/dist/registry/source-forms.js +202 -0
  260. package/dist/registry/targets/artifactory.d.ts +28 -0
  261. package/dist/registry/targets/artifactory.d.ts.map +1 -0
  262. package/dist/registry/targets/artifactory.js +206 -0
  263. package/dist/registry/targets/fake-artifactory.d.ts +30 -0
  264. package/dist/registry/targets/fake-artifactory.d.ts.map +1 -0
  265. package/dist/registry/targets/fake-artifactory.js +142 -0
  266. package/dist/registry/targets/fake-gitlab.d.ts +39 -0
  267. package/dist/registry/targets/fake-gitlab.d.ts.map +1 -0
  268. package/dist/registry/targets/fake-gitlab.js +136 -0
  269. package/dist/registry/targets/fake-hcp.d.ts +50 -0
  270. package/dist/registry/targets/fake-hcp.d.ts.map +1 -0
  271. package/dist/registry/targets/fake-hcp.js +202 -0
  272. package/dist/registry/targets/fake-http.d.ts +31 -0
  273. package/dist/registry/targets/fake-http.d.ts.map +1 -0
  274. package/dist/registry/targets/fake-http.js +87 -0
  275. package/dist/registry/targets/fake-nexus.d.ts +27 -0
  276. package/dist/registry/targets/fake-nexus.d.ts.map +1 -0
  277. package/dist/registry/targets/fake-nexus.js +137 -0
  278. package/dist/registry/targets/gitlab.d.ts +53 -0
  279. package/dist/registry/targets/gitlab.d.ts.map +1 -0
  280. package/dist/registry/targets/gitlab.js +187 -0
  281. package/dist/registry/targets/hcp.d.ts +29 -0
  282. package/dist/registry/targets/hcp.d.ts.map +1 -0
  283. package/dist/registry/targets/hcp.js +310 -0
  284. package/dist/registry/targets/hosted.d.ts +62 -0
  285. package/dist/registry/targets/hosted.d.ts.map +1 -0
  286. package/dist/registry/targets/hosted.js +74 -0
  287. package/dist/registry/targets/index.d.ts +18 -0
  288. package/dist/registry/targets/index.d.ts.map +1 -0
  289. package/dist/registry/targets/index.js +17 -0
  290. package/dist/registry/targets/module-target.d.ts +68 -0
  291. package/dist/registry/targets/module-target.d.ts.map +1 -0
  292. package/dist/registry/targets/module-target.js +63 -0
  293. package/dist/registry/targets/nexus.d.ts +43 -0
  294. package/dist/registry/targets/nexus.d.ts.map +1 -0
  295. package/dist/registry/targets/nexus.js +250 -0
  296. package/dist/registry/targets/zip.d.ts +55 -0
  297. package/dist/registry/targets/zip.d.ts.map +1 -0
  298. package/dist/registry/targets/zip.js +278 -0
  299. package/dist/registry/writer.d.ts +101 -0
  300. package/dist/registry/writer.d.ts.map +1 -0
  301. package/dist/registry/writer.js +109 -0
  302. package/dist/report.d.ts +15 -6
  303. package/dist/report.d.ts.map +1 -1
  304. package/dist/report.js +28 -55
  305. package/dist/rules/record-store.ts +122 -0
  306. package/dist/rules/tf001.ts +2 -2
  307. package/dist/rules/tf024.ts +105 -22
  308. package/dist/rules/tf046.ts +204 -0
  309. package/dist/rules/tf047.ts +114 -0
  310. package/dist/skills/chant-terraform.md +1 -1
  311. package/dist/state/azure-blob.d.ts +9 -0
  312. package/dist/state/azure-blob.d.ts.map +1 -0
  313. package/dist/state/azure-blob.js +8 -0
  314. package/dist/state/backend.d.ts +158 -0
  315. package/dist/state/backend.d.ts.map +1 -0
  316. package/dist/state/backend.js +406 -0
  317. package/dist/state/exec.d.ts +39 -0
  318. package/dist/state/exec.d.ts.map +1 -0
  319. package/dist/state/exec.js +80 -0
  320. package/dist/state/gcs.d.ts +8 -0
  321. package/dist/state/gcs.d.ts.map +1 -0
  322. package/dist/state/gcs.js +7 -0
  323. package/dist/state/index.d.ts +15 -0
  324. package/dist/state/index.d.ts.map +1 -0
  325. package/dist/state/index.js +14 -0
  326. package/dist/state/locks.d.ts +105 -0
  327. package/dist/state/locks.d.ts.map +1 -0
  328. package/dist/state/locks.js +130 -0
  329. package/dist/state/object-store.d.ts +8 -0
  330. package/dist/state/object-store.d.ts.map +1 -0
  331. package/dist/state/object-store.js +7 -0
  332. package/dist/state/root-states.d.ts +131 -0
  333. package/dist/state/root-states.d.ts.map +1 -0
  334. package/dist/state/root-states.js +241 -0
  335. package/dist/state/s3.d.ts +21 -0
  336. package/dist/state/s3.d.ts.map +1 -0
  337. package/dist/state/s3.js +19 -0
  338. package/dist/state/store.d.ts +51 -0
  339. package/dist/state/store.d.ts.map +1 -0
  340. package/dist/state/store.js +185 -0
  341. package/dist/state-ops.d.ts +255 -0
  342. package/dist/state-ops.d.ts.map +1 -0
  343. package/dist/state-ops.js +305 -0
  344. package/dist/state-reads.d.ts +57 -0
  345. package/dist/state-reads.d.ts.map +1 -0
  346. package/dist/state-reads.js +104 -0
  347. package/dist/terragrunt/affected.d.ts.map +1 -1
  348. package/dist/terragrunt/affected.js +2 -1
  349. package/dist/used-sources.d.ts +54 -0
  350. package/dist/used-sources.d.ts.map +1 -0
  351. package/dist/used-sources.js +157 -0
  352. package/package.json +24 -4
  353. package/src/__fixtures__/ACCEPTANCE.md +69 -7
  354. package/src/__fixtures__/compose/apply-failed-in-app.jsonl +12 -0
  355. package/src/__fixtures__/compose/state-app.json +66 -0
  356. package/src/__fixtures__/compose/state-edge.json +40 -0
  357. package/src/__fixtures__/compose/state-net.json +58 -0
  358. package/src/__fixtures__/compose/three-app.plan.json +184 -0
  359. package/src/__fixtures__/compose/three-composed.plan.json +509 -0
  360. package/src/__fixtures__/compose/three-edge.plan.json +144 -0
  361. package/src/__fixtures__/compose/three-net.plan.json +174 -0
  362. package/src/__fixtures__/compose/unknown-composed.plan.json +228 -0
  363. package/src/__fixtures__/compose/working-after-apply.json +145 -0
  364. package/src/__fixtures__/config-blocks/app/main.tf +61 -0
  365. package/src/__fixtures__/config-blocks/app/modules/network/main.tf +4 -0
  366. package/src/__fixtures__/config-blocks/app-json/main.tf.json +39 -0
  367. package/src/__fixtures__/config-files/shadow/main.tf +3 -0
  368. package/src/__fixtures__/config-files/shadow/main.tofu +3 -0
  369. package/src/__fixtures__/config-files/shadow/other.tf +3 -0
  370. package/src/__fixtures__/config-files/tf/main.tf +11 -0
  371. package/src/__fixtures__/config-files/tf-json/main.tf.json +18 -0
  372. package/src/__fixtures__/config-files/tofu/main.tofu +11 -0
  373. package/src/__fixtures__/config-files/tofu-json/main.tofu.json +18 -0
  374. package/src/__fixtures__/config-files/tofu-only/main.tofu +3 -0
  375. package/src/__fixtures__/drift-record/README.md +16 -0
  376. package/src/__fixtures__/drift-record/live-plan-sections.json +27 -0
  377. package/src/__fixtures__/drift-rows/live-root-plan.json +70 -0
  378. package/src/__fixtures__/drift-rows/refresh-plan.json +61 -0
  379. package/src/__fixtures__/instance-forms/app/main.tf +110 -0
  380. package/src/__fixtures__/instance-forms/app/modules/svc/main.tf +3 -0
  381. package/src/__fixtures__/instance-vars/app/main.tf +52 -0
  382. package/src/__fixtures__/instance-vars/app/modules/app/main.tf +12 -0
  383. package/src/__fixtures__/instance-vars/app/modules/edge/main.tf +3 -0
  384. package/src/__fixtures__/instance-vars/app/terraform.tfvars +1 -0
  385. package/src/__fixtures__/live-module-keyed/live-graph.json +259 -0
  386. package/src/__fixtures__/live-module-keyed/live-plan.json +87 -0
  387. package/src/__fixtures__/live-module-keyed/root/estate.chdf.hcl +1 -0
  388. package/src/__fixtures__/live-module-keyed/root/main.tf +21 -0
  389. package/src/__fixtures__/live-module-keyed/root/modules/app/main.tf +17 -0
  390. package/src/__fixtures__/live-module-keyed/root/modules/edge/main.tf +7 -0
  391. package/src/__fixtures__/live-module-keyed/root/versions.tf +30 -0
  392. package/src/__fixtures__/live-module-keyed/show-state.json +146 -0
  393. package/src/__fixtures__/pr-loop/README.md +50 -0
  394. package/src/__fixtures__/pr-loop/chant.config.json +13 -0
  395. package/src/__fixtures__/pr-loop/estate.component.ts +10 -0
  396. package/src/__fixtures__/pr-loop/roots/a/main.tf +11 -0
  397. package/src/__fixtures__/pr-loop/roots/app/main.tf +19 -0
  398. package/src/__fixtures__/pr-loop/roots/b/main.tf +7 -0
  399. package/src/__fixtures__/pr-loop/roots/dns/main.tf +3 -0
  400. package/src/__fixtures__/pr-loop/roots/net/main.tf +7 -0
  401. package/src/__fixtures__/report/applied.show.json +51 -0
  402. package/src/__fixtures__/report/generate.ts +63 -0
  403. package/src/__fixtures__/report/plan.show.json +200 -0
  404. package/src/__fixtures__/report/tf-apply/change-set.json +170 -0
  405. package/src/__fixtures__/report/tf-apply/report.json +338 -0
  406. package/src/__fixtures__/report/tf-plan/change-set.json +170 -0
  407. package/src/__fixtures__/report/tf-plan/report.json +320 -0
  408. package/src/__fixtures__/three-cloud-azure.graph.json +290 -0
  409. package/src/__fixtures__/used-sources/root/extra.tofu +1 -0
  410. package/src/__fixtures__/used-sources/root/main.tf +31 -0
  411. package/src/__fixtures__/used-sources/root/modules/net/main.tf +14 -0
  412. package/src/__fixtures__/used-sources/root/modules/net/versions.tf.json +7 -0
  413. package/src/__fixtures__/used-sources/unit/terragrunt.hcl +3 -0
  414. package/src/applied-state.ts +52 -0
  415. package/src/backend-config.test.ts +105 -0
  416. package/src/backend-config.ts +133 -0
  417. package/src/behaviour/kinds.ts +4 -0
  418. package/src/behaviour/predict.ts +5 -2
  419. package/src/behaviour/reference-catalog.plugin.test.ts +65 -0
  420. package/src/behaviour/reference-catalog.ts +16 -5
  421. package/src/behaviour/request.ts +3 -2
  422. package/src/change-set-instances.test.ts +98 -0
  423. package/src/changed-roots.ts +14 -16
  424. package/src/ci-setup.ts +42 -0
  425. package/src/commands.ts +4 -3
  426. package/src/components/capability-plugin.test.ts +198 -19
  427. package/src/components/capability-plugin.ts +383 -35
  428. package/src/components/change-set-graph.e2e.test.ts +11 -5
  429. package/src/components/compose-group.test.ts +486 -0
  430. package/src/components/compose-group.ts +489 -0
  431. package/src/components/demo-composed-roots.e2e.test.ts +440 -0
  432. package/src/components/member-outputs.test.ts +76 -0
  433. package/src/components/placeholder-preview.test.ts +147 -0
  434. package/src/components/placeholder-preview.ts +257 -0
  435. package/src/components/pr-loop-compose.tofu.e2e.test.ts +363 -0
  436. package/src/components/pr-loop.tofu.test.ts +113 -57
  437. package/src/components/release-inputs.ts +69 -0
  438. package/src/components/trace-facts.test.ts +21 -0
  439. package/src/components/trace-facts.ts +27 -0
  440. package/src/compose/apply-events.test.ts +62 -0
  441. package/src/compose/apply-events.ts +100 -0
  442. package/src/compose/estate.choudoufu.e2e.test.ts +112 -0
  443. package/src/compose/estate.test.ts +421 -0
  444. package/src/compose/estate.ts +443 -0
  445. package/src/compose/hcl.ts +311 -0
  446. package/src/compose/module-address.ts +49 -0
  447. package/src/compose/root.test.ts +416 -0
  448. package/src/compose/root.ts +592 -0
  449. package/src/compose/run.test.ts +440 -0
  450. package/src/compose/run.tofu.test.ts +176 -0
  451. package/src/compose/run.ts +527 -0
  452. package/src/compose/slice.test.ts +184 -0
  453. package/src/compose/slice.ts +290 -0
  454. package/src/compose/state-address.ts +157 -0
  455. package/src/compose/state-merge.test.ts +131 -0
  456. package/src/compose/state-merge.ts +148 -0
  457. package/src/compose.test.ts +248 -0
  458. package/src/compose.ts +627 -0
  459. package/src/composites/terraform-apply-op.test.ts +9 -0
  460. package/src/composites/terraform-apply-op.ts +7 -3
  461. package/src/cone.ts +138 -0
  462. package/src/config-file-listers.test.ts +265 -0
  463. package/src/config.ts +44 -9
  464. package/src/cost-hold.ts +29 -0
  465. package/src/describe-resources.engine-facts.test.ts +411 -0
  466. package/src/describe-resources.live-module-keyed.test.ts +290 -0
  467. package/src/describe-resources.ts +416 -70
  468. package/src/drift-read.ts +284 -0
  469. package/src/drift-record.test.ts +223 -0
  470. package/src/drift-record.ts +455 -0
  471. package/src/drift-rows.test.ts +135 -0
  472. package/src/drift-rows.ts +137 -0
  473. package/src/exit-codes.test.ts +23 -0
  474. package/src/exit-codes.ts +59 -0
  475. package/src/hcl/config-blocks.test.ts +174 -0
  476. package/src/hcl/edges.ts +21 -1
  477. package/src/hcl/files.ts +234 -0
  478. package/src/hcl/instance-forms.test.ts +94 -0
  479. package/src/hcl/instances.test.ts +137 -0
  480. package/src/hcl/instances.ts +422 -0
  481. package/src/hcl/parse.test.ts +139 -1
  482. package/src/hcl/parse.ts +149 -16
  483. package/src/hcl/roots.ts +6 -0
  484. package/src/hcl/suppressions.ts +8 -2
  485. package/src/index.ts +79 -0
  486. package/src/lint/audit-catalog.ts +29 -3
  487. package/src/lint/audit-lineage.ts +14 -2
  488. package/src/lint/post-synth/fixtures/TF024/live-with-record-store/main.tf +18 -0
  489. package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/estate.chdf.hcl +1 -0
  490. package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/main.tf +10 -0
  491. package/src/lint/post-synth/fixtures/TF046/azurerm-no-resource-group/main.tf +15 -0
  492. package/src/lint/post-synth/fixtures/TF046/backend-config-only/main.tf +14 -0
  493. package/src/lint/post-synth/fixtures/TF046/derived-s3/backend.tf +7 -0
  494. package/src/lint/post-synth/fixtures/TF046/derived-s3/main.tf +9 -0
  495. package/src/lint/post-synth/fixtures/TF046/explicit-local/main.tf +16 -0
  496. package/src/lint/post-synth/fixtures/TF046/explicit-s3-beside-local-backend/main.tf +15 -0
  497. package/src/lint/post-synth/fixtures/TF046/implied-local/main.tf +9 -0
  498. package/src/lint/post-synth/fixtures/TF046/sidecar-implied/estate.chdf.hcl +1 -0
  499. package/src/lint/post-synth/fixtures/TF046/sidecar-implied/main.tf +5 -0
  500. package/src/lint/post-synth/fixtures/TF047/clear/main.tf +12 -0
  501. package/src/lint/post-synth/fixtures/TF047/refused/main.tf +12 -0
  502. package/src/lint/post-synth/fixtures/TF047/unknown-module/main.tf +12 -0
  503. package/src/lint/post-synth/fixtures/TF047/unknown-version/main.tf +12 -0
  504. package/src/lint/post-synth/index.ts +4 -0
  505. package/src/lint/post-synth/record-store.ts +122 -0
  506. package/src/lint/post-synth/tf001.ts +2 -2
  507. package/src/lint/post-synth/tf024.test.ts +26 -5
  508. package/src/lint/post-synth/tf024.ts +105 -22
  509. package/src/lint/post-synth/tf046.test.ts +137 -0
  510. package/src/lint/post-synth/tf046.ts +204 -0
  511. package/src/lint/post-synth/tf047.test.ts +56 -0
  512. package/src/lint/post-synth/tf047.ts +114 -0
  513. package/src/live-plan-reasons.ts +54 -0
  514. package/src/lock-rows.test.ts +70 -0
  515. package/src/lock-rows.ts +82 -0
  516. package/src/lock-timeout.ts +34 -0
  517. package/src/lsp/completions.test.ts +1 -1
  518. package/src/lsp/option-keys.ts +2 -0
  519. package/src/module-catalog/index.test.ts +93 -0
  520. package/src/module-catalog/index.ts +159 -0
  521. package/src/module-catalog/refresh-cli.ts +68 -0
  522. package/src/module-catalog/static-tier.ts +1659 -0
  523. package/src/module-catalog/types.ts +88 -0
  524. package/src/op/activities/choudoufu-plan-set-op.test.ts +111 -0
  525. package/src/op/activities/index.ts +9 -0
  526. package/src/op/activities/live-detect.ts +55 -26
  527. package/src/op/activities/pin-rollout.ts +3 -0
  528. package/src/op/activities/registry.test.ts +2 -1
  529. package/src/op/activities/terraform.test.ts +55 -0
  530. package/src/op/activities/terraform.ts +312 -27
  531. package/src/op/activity-contracts.test.ts +1 -0
  532. package/src/op/activity-contracts.ts +24 -1
  533. package/src/op/builders.ts +14 -0
  534. package/src/op-wave-step.test.ts +109 -0
  535. package/src/pin/__fixtures__/consumers/live/dev/main.tf +4 -0
  536. package/src/pin/__fixtures__/consumers/live/prod/main.tf +21 -0
  537. package/src/pin/__fixtures__/consumers/units/vpc/terragrunt.hcl +7 -0
  538. package/src/pin/bundle.test.ts +2 -0
  539. package/src/pin/consumers.test.ts +57 -0
  540. package/src/pin/consumers.ts +95 -0
  541. package/src/pin/edit.ts +30 -0
  542. package/src/pin/rollout.ts +16 -11
  543. package/src/plan-shape.test.ts +148 -0
  544. package/src/plan-shape.ts +128 -0
  545. package/src/plugin.ts +39 -19
  546. package/src/registry/__fixtures__/docs/documented/.terraform-docs.yml +3 -0
  547. package/src/registry/__fixtures__/docs/documented/README.md +1 -0
  548. package/src/registry/__fixtures__/docs/documented/main.tf +8 -0
  549. package/src/registry/__fixtures__/docs/json/main.tf.json +14 -0
  550. package/src/registry/__fixtures__/docs/vpc/README.md +3 -0
  551. package/src/registry/__fixtures__/docs/vpc/examples/complete/main.tf +5 -0
  552. package/src/registry/__fixtures__/docs/vpc/examples/simple/README.md +1 -0
  553. package/src/registry/__fixtures__/docs/vpc/examples/simple/main.tf +4 -0
  554. package/src/registry/__fixtures__/docs/vpc/main.tf +14 -0
  555. package/src/registry/__fixtures__/docs/vpc/outputs.tf +9 -0
  556. package/src/registry/__fixtures__/docs/vpc/variables.tf +16 -0
  557. package/src/registry/__fixtures__/proxied-root/main.tf +12 -0
  558. package/src/registry/address.ts +100 -0
  559. package/src/registry/catalog-read.ts +56 -0
  560. package/src/registry/deprecated.test.ts +139 -0
  561. package/src/registry/deprecated.ts +76 -0
  562. package/src/registry/docs.test.ts +140 -0
  563. package/src/registry/docs.ts +299 -0
  564. package/src/registry/edge/bundle.test.ts +50 -0
  565. package/src/registry/edge/edge.test.ts +176 -0
  566. package/src/registry/edge/edge.ts +211 -0
  567. package/src/registry/edge/index.ts +10 -0
  568. package/src/registry/edge/lambda-edge.ts +127 -0
  569. package/src/registry/edge/sigv4-web.ts +79 -0
  570. package/src/registry/edge/token-source.test.ts +258 -0
  571. package/src/registry/edge/token-source.ts +169 -0
  572. package/src/registry/edge/worker.ts +61 -0
  573. package/src/registry/handler.ts +89 -0
  574. package/src/registry/headers-file.ts +61 -0
  575. package/src/registry/index.ts +31 -0
  576. package/src/registry/mirror-paths.ts +10 -0
  577. package/src/registry/mirror.test.ts +198 -0
  578. package/src/registry/mirror.ts +395 -0
  579. package/src/registry/proxies.test.ts +153 -0
  580. package/src/registry/proxies.ts +132 -0
  581. package/src/registry/reader.ts +225 -0
  582. package/src/registry/registry.test.ts +262 -0
  583. package/src/registry/semver.ts +34 -0
  584. package/src/registry/serve.ts +82 -0
  585. package/src/registry/source-forms.test.ts +87 -0
  586. package/src/registry/source-forms.ts +216 -0
  587. package/src/registry/targets/artifactory.test.ts +248 -0
  588. package/src/registry/targets/artifactory.ts +257 -0
  589. package/src/registry/targets/fake-artifactory.ts +158 -0
  590. package/src/registry/targets/fake-gitlab.ts +161 -0
  591. package/src/registry/targets/fake-hcp.ts +233 -0
  592. package/src/registry/targets/fake-http.ts +97 -0
  593. package/src/registry/targets/fake-nexus.ts +152 -0
  594. package/src/registry/targets/gitlab.test.ts +208 -0
  595. package/src/registry/targets/gitlab.ts +223 -0
  596. package/src/registry/targets/hcp.test.ts +230 -0
  597. package/src/registry/targets/hcp.ts +360 -0
  598. package/src/registry/targets/hosted.ts +117 -0
  599. package/src/registry/targets/index.ts +17 -0
  600. package/src/registry/targets/module-target.ts +131 -0
  601. package/src/registry/targets/nexus.test.ts +220 -0
  602. package/src/registry/targets/nexus.ts +314 -0
  603. package/src/registry/targets/zip.test.ts +65 -0
  604. package/src/registry/targets/zip.ts +287 -0
  605. package/src/registry/writer.ts +155 -0
  606. package/src/report.test.ts +82 -1
  607. package/src/report.ts +31 -54
  608. package/src/signal-only-root.test.ts +72 -0
  609. package/src/skills/chant-terraform.md +1 -1
  610. package/src/state/azure-blob.ts +8 -0
  611. package/src/state/backend-verdict.test.ts +187 -0
  612. package/src/state/backend.test.ts +127 -0
  613. package/src/state/backend.ts +488 -0
  614. package/src/state/exec.ts +111 -0
  615. package/src/state/gcs.ts +7 -0
  616. package/src/state/index.ts +15 -0
  617. package/src/state/locks.test.ts +84 -0
  618. package/src/state/locks.ts +201 -0
  619. package/src/state/object-store.ts +7 -0
  620. package/src/state/root-states-floci.e2e.test.ts +66 -0
  621. package/src/state/root-states.test.ts +462 -0
  622. package/src/state/root-states.ts +309 -0
  623. package/src/state/s3.ts +21 -0
  624. package/src/state/store.ts +225 -0
  625. package/src/state-ops.test.ts +182 -0
  626. package/src/state-ops.ts +462 -0
  627. package/src/state-reads.ts +116 -0
  628. package/src/terragrunt/affected.ts +2 -1
  629. package/src/used-sources.test.ts +75 -0
  630. package/src/used-sources.ts +204 -0
  631. package/src/workspace-kinds.test.ts +4 -17
  632. package/workspace-kinds.json +1 -24
  633. package/dist/workspace-member.d.ts +0 -30
  634. package/dist/workspace-member.d.ts.map +0 -1
  635. package/dist/workspace-member.js +0 -91
  636. package/schemas/pulumi.schema.json +0 -34
  637. package/src/testdata/pulumi-member/Pulumi.yaml +0 -2
  638. package/src/testdata/pulumi-member/stack-export.json +0 -53
  639. package/src/testdata/pulumi-unexported/Pulumi.yaml +0 -2
  640. package/src/workspace-member.test.ts +0 -89
  641. package/src/workspace-member.ts +0 -126
@@ -0,0 +1,489 @@
1
+ /**
2
+ * `terraform-apply`'s group methods (chant#4447, ws-107): core forms a group
3
+ * of `terraform-apply` components that read each other, and these plan it
4
+ * as one plan and apply it as one apply of a composed root, so a value one
5
+ * root creates reaches its readers inside that apply.
6
+ *
7
+ * - `groupReads` reports to core each member's `terraform_remote_state` and
8
+ * `terraform_estate_outputs` reads of another, as the composition finds
9
+ * them, so roots wired in HCL alone form a group. Nothing while
10
+ * `terraform.compose.enabled` is unset.
11
+ * - `groupFallback` answers `off` while `terraform.compose.enabled` is unset,
12
+ * else builds the {@link Composition} (#4438) from the files, the config
13
+ * and each member's cloud identity, and answers its fallback, if any. A
14
+ * member the composition leaves out is dropped with its own kind and
15
+ * reason, and core forms the group again from the rest (#4419 ruling 1).
16
+ * A composition of estates also asks `choudoufu version` (#4445).
17
+ * Nothing plans.
18
+ * - `planGroup` builds the composition again, plans it once with
19
+ * `ComposedRun` (#4444) or, for estates, `EstateRun` (#4445), and answers
20
+ * each member's part (#4442): its change-set part, planned outputs and
21
+ * shape, with its own plan's digest. The group's digest is the composed
22
+ * plan's with the generated files. The run is released before it returns,
23
+ * with no lock taken, so a plan that core then refuses or makes wait
24
+ * leaves no lock held and no file changed.
25
+ * - `runGroup` plans once more, this time under each state's lock, and
26
+ * applies only when that plan's digest is the one `planGroup` answered;
27
+ * otherwise it throws a moved plan and nothing applies. Whatever happens,
28
+ * it releases the run. Each member's outputs come from the run: its part
29
+ * of the working state's `show -json` for roots with state, the post-apply
30
+ * plan's `prior_state` for estates.
31
+ *
32
+ * Each member's identity is `ci.identity.cloud` resolved for its root as
33
+ * the target (`resolveTargetCloudIdentity`, #4332), by the root's key or its
34
+ * directory, whichever a rule's target glob matches, compared by
35
+ * `identityKey`, so members that apply under different identities do not
36
+ * compose (#4438's `identity`).
37
+ */
38
+
39
+ import { mkdtempSync, rmSync } from "node:fs";
40
+ import { tmpdir } from "node:os";
41
+ import { dirname, isAbsolute, join, resolve } from "node:path";
42
+ import { loadChantConfigUpward } from "@intentius/chant/config";
43
+ import type {
44
+ CapabilityGroupOutcome,
45
+ CapabilityGroupPlan,
46
+ CapabilityPlan,
47
+ GroupContext,
48
+ GroupDrop,
49
+ GroupFallback,
50
+ GroupMember,
51
+ GroupMemberOutcome,
52
+ GroupRun,
53
+ ReportedGroupRead,
54
+ } from "@intentius/chant/components/capability";
55
+ import type { ChangeSetPart } from "@intentius/chant/change-set";
56
+ import type { DriverComponent } from "@intentius/chant/components/driver";
57
+ import { wiringReferencesAt } from "@intentius/chant/components/driver";
58
+ import { computePlanDigest } from "@intentius/chant/lifecycle/plan-digest";
59
+ import { declaredTargetCloudIdentity, type TargetCloudIdentityLookup } from "@intentius/chant/workspace/ci-cloud-identity";
60
+ import { COMPOSE_STEP_KIND, composeOff, compositionIfEnabled, identityKey, rootHclReads, type Composition } from "../compose";
61
+ import type { TerraformConfig } from "../config";
62
+ import { terraformBinary, terraformOutputs } from "../op/activities/terraform";
63
+ import { runBinary, type BinaryExec } from "../state/exec";
64
+ import { estateVersionFallback, EstateRun, type ComposedRunner } from "../compose/estate";
65
+ import { ComposedRun } from "../compose/run";
66
+ import { relPath, rootTexts } from "../compose/hcl";
67
+
68
+ /** The step input as the group methods read it; `TerraformApplyStepInput` in ./capability-plugin.ts. */
69
+ interface StepInput {
70
+ root: string;
71
+ vars?: Record<string, unknown>;
72
+ cwd?: string;
73
+ }
74
+
75
+ /** The project a group's roots belong to. */
76
+ export interface ComposeProject {
77
+ /** Where `chant.config.*` was found: roots resolve against it and `.chant-compose/` is written there. */
78
+ projectRoot: string;
79
+ terraform: TerraformConfig;
80
+ }
81
+
82
+ /** What a runner is built from. */
83
+ export interface GroupRunnerOptions extends ComposeProject {
84
+ composition: Composition;
85
+ /** Take each state's lock: `runGroup` does, `planGroup` does not. */
86
+ lock: boolean;
87
+ /** Each member's step `vars` as written, by component. */
88
+ vars: Record<string, Record<string, unknown> | undefined>;
89
+ /** The outputs of components outside the group that a member's `vars` read. */
90
+ componentOutputs: Record<string, Record<string, unknown>>;
91
+ env: NodeJS.ProcessEnv;
92
+ /** A directory of the run's own, removed after the run is released. */
93
+ work: string;
94
+ }
95
+
96
+ /** What `planGroup` hands `runGroup` through `GroupRun.artifact`. */
97
+ interface ComposedArtifact {
98
+ /** The composed plan's digest `planGroup` answered (`CapabilityGroupPlan.digest`). */
99
+ digest: string;
100
+ estate: boolean;
101
+ }
102
+
103
+ /** What the group methods use, each replaceable in a test. */
104
+ export interface ComposeGroupDeps {
105
+ /** The project's config. Default: `loadChantConfigUpward` from the current directory. */
106
+ project?: () => Promise<ComposeProject>;
107
+ /** The run for a composition. Default: `EstateRun` when `estate` is set, else `ComposedRun`. */
108
+ runner?: (o: GroupRunnerOptions) => ComposedRunner;
109
+ /**
110
+ * Each target's cloud identity for `env` (`resolveTargetCloudIdentity`,
111
+ * #4332), or undefined when there is no workspace to read. Default:
112
+ * `declaredTargetCloudIdentity` from the project root.
113
+ */
114
+ identity?: (projectRoot: string, env: string) => Promise<TargetCloudIdentityLookup | undefined>;
115
+ /** How `choudoufu version` runs. Default `runBinary`. */
116
+ exec?: BinaryExec;
117
+ /** A root's outputs as its state holds them, for a member whose part of the working state could not be shown. Default `terraformOutputs`. */
118
+ outputs?: (root: string) => Promise<Record<string, unknown>>;
119
+ env?: NodeJS.ProcessEnv;
120
+ log?: (line: string) => void;
121
+ }
122
+
123
+ /** The group methods `terraform-apply` declares. */
124
+ export interface ComposeGroupMethods {
125
+ groupReads(ctx: GroupContext, members: readonly GroupMember<StepInput>[]): Promise<ReportedGroupRead[]>;
126
+ groupFallback(ctx: GroupContext, members: readonly GroupMember<StepInput>[]): Promise<GroupFallback | undefined>;
127
+ planGroup(ctx: GroupContext, members: readonly GroupMember<StepInput>[]): Promise<CapabilityGroupPlan>;
128
+ runGroup(ctx: GroupContext, run: GroupRun, members: readonly GroupMember<StepInput>[]): Promise<CapabilityGroupOutcome>;
129
+ }
130
+
131
+ async function loadProject(): Promise<ComposeProject> {
132
+ const start = resolve(process.cwd());
133
+ const { config, configPath } = await loadChantConfigUpward(start);
134
+ const projectRoot = configPath ? dirname(configPath) : start;
135
+ const terraform = (config as { terraform?: TerraformConfig }).terraform;
136
+ return { projectRoot, terraform: { ...(terraform ?? {}), roots: terraform?.roots ?? {} } as TerraformConfig };
137
+ }
138
+
139
+ function defaultRunner(o: GroupRunnerOptions, log?: (line: string) => void): ComposedRunner {
140
+ if (o.composition.estate) {
141
+ return new EstateRun({ projectRoot: o.projectRoot, composition: o.composition, terraform: o.terraform, env: o.env, work: o.work, ...(log ? { log } : {}) });
142
+ }
143
+ return new ComposedRun({
144
+ projectRoot: o.projectRoot,
145
+ composition: o.composition,
146
+ terraform: o.terraform,
147
+ binary: { path: terraformBinary(o.terraform) },
148
+ env: o.env,
149
+ work: o.work,
150
+ lock: o.lock,
151
+ ...(o.env.GITHUB_RUN_ID ? { runId: `github-run-${o.env.GITHUB_RUN_ID}` } : {}),
152
+ vars: o.vars,
153
+ componentOutputs: o.componentOutputs,
154
+ // A state that could not be written is kept here when its backend cannot take it; it outlives the run's own directory.
155
+ reportDir: join(o.projectRoot, ".chant", "compose", "unwritten"),
156
+ ...(log ? { log } : {}),
157
+ });
158
+ }
159
+
160
+ /** The members as components, each deploying its one step, for `compositionIfEnabled`. */
161
+ export function memberComponents(members: readonly GroupMember<StepInput>[]): DriverComponent[] {
162
+ return members.map((m) => ({
163
+ name: m.component,
164
+ ...(m.dependsOn ? { dependsOn: [...m.dependsOn] } : {}),
165
+ deploy: [{ phase: "Apply", steps: [{ ...m.step, kind: COMPOSE_STEP_KIND }] }],
166
+ }));
167
+ }
168
+
169
+ /**
170
+ * Each member's identity as `compositionFor` compares it: `identityKey` of
171
+ * `lookup` for the member's root, which a rule's target globs match by the
172
+ * root's key in `terraform.roots` or by its directory relative to the
173
+ * project (the first declared rule that either matches wins). Undefined
174
+ * without a lookup, so no member falls back as `identity`.
175
+ */
176
+ export function memberIdentity(
177
+ project: ComposeProject,
178
+ members: readonly GroupMember<StepInput>[],
179
+ lookup: TargetCloudIdentityLookup | undefined,
180
+ ): ((member: string) => string | undefined) | undefined {
181
+ if (!lookup) return undefined;
182
+ const names = (root: string): string[] => {
183
+ const dir = project.terraform.roots[root]?.dir;
184
+ if (dir === undefined) return [root];
185
+ return [root, relPath(project.projectRoot, isAbsolute(dir) ? dir : resolve(project.projectRoot, dir))];
186
+ };
187
+ const keys = new Map(members.map((m) => [m.component, identityKey(lookup(names(m.step.root as string)))] as const));
188
+ return (member) => keys.get(member);
189
+ }
190
+
191
+ /**
192
+ * The composition of exactly the group's members (core has already formed the
193
+ * group), or undefined when `terraform.compose` is not enabled.
194
+ */
195
+ export function groupComposition(
196
+ project: ComposeProject,
197
+ members: readonly GroupMember<StepInput>[],
198
+ env: NodeJS.ProcessEnv,
199
+ identity?: (member: string) => string | undefined,
200
+ ): Composition | undefined {
201
+ const targets = Object.fromEntries(members.flatMap((m) => (m.ctx.targets?.length ? [[m.component, m.ctx.targets] as const] : [])));
202
+ return compositionIfEnabled({
203
+ projectRoot: project.projectRoot,
204
+ terraform: project.terraform,
205
+ components: memberComponents(members),
206
+ selected: new Set(members.map((m) => m.component)),
207
+ env,
208
+ targets,
209
+ ...(identity ? { identity } : {}),
210
+ });
211
+ }
212
+
213
+ const escapeRe = (s: string): string => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
214
+
215
+ /** The data source and the attribute that holds its outputs, for each kind of HCL read. */
216
+ const HCL_READ = { "remote-state": ["terraform_remote_state", "outputs"], "estate-outputs": ["terraform_estate_outputs", "values"] } as const;
217
+
218
+ /** The outputs the texts read off `data.<type>.<name>.<field>`, by attribute or by index, sorted. */
219
+ function outputsRead(texts: readonly string[], type: string, name: string, field: string): string[] {
220
+ const re = new RegExp(`\\bdata\\.${type}\\.${escapeRe(name)}\\.${field}(?:\\.([A-Za-z_][\\w-]*)|\\[\\s*\\\\?"([^"\\\\]+)\\\\?"\\s*\\])`, "g");
221
+ const out = new Set<string>();
222
+ for (const text of texts) for (const m of text.matchAll(re)) out.add((m[1] ?? m[2])!);
223
+ return [...out].sort();
224
+ }
225
+
226
+ /**
227
+ * The members' HCL reads of each other (#4447): each `terraform_remote_state`
228
+ * block whose state is another member's backend, and each
229
+ * `terraform_estate_outputs` block naming another member's estate, as the
230
+ * composition finds them. Each output the reader's code takes off the block
231
+ * is one read, `<upstream>.<output>` as a `stackOutput()` read is named; a
232
+ * block whose outputs the code does not name one by one is one read with
233
+ * the block's address as `ref`. Core cannot see these in the step's `vars`.
234
+ */
235
+ export function hclGroupReads(project: ComposeProject, members: readonly GroupMember<StepInput>[]): ReportedGroupRead[] {
236
+ const reads = rootHclReads({ projectRoot: project.projectRoot, terraform: project.terraform, components: memberComponents(members) });
237
+ const out: ReportedGroupRead[] = [];
238
+ for (const [reader, rs] of reads) {
239
+ const m = members.find((x) => x.component === reader);
240
+ const dir = m ? project.terraform.roots[m.step.root as string]?.dir : undefined;
241
+ const texts = dir === undefined ? [] : rootTexts(isAbsolute(dir) ? dir : resolve(project.projectRoot, dir), project.terraform.binary).map((t) => t.text);
242
+ for (const r of rs) {
243
+ if (r.via === "vars") continue;
244
+ const [type, field] = HCL_READ[r.via];
245
+ const named = outputsRead(texts, type, r.name, field);
246
+ if (named.length === 0) out.push({ reader, upstream: r.upstream, ref: `data.${type}.${r.name}` });
247
+ for (const output of named) out.push({ reader, upstream: r.upstream, ref: `${r.upstream}.${output}`, output });
248
+ }
249
+ }
250
+ return out;
251
+ }
252
+
253
+ /**
254
+ * What core is told of the group (#4419 ruling 1): the whole change's
255
+ * fallback when it is not `one-root`; else each member the composition left
256
+ * out as a drop with its own kind and reason, so core groups the rest
257
+ * again without them; else `one-root`. Undefined when every member
258
+ * composes.
259
+ */
260
+ export function groupFallbackOf(c: Composition, members: readonly string[]): GroupFallback | undefined {
261
+ if (c.fallback && c.fallback.kind !== "one-root") return { kind: c.fallback.kind, reason: c.fallback.reason };
262
+ const drop: GroupDrop[] = c.fallbacks.flatMap((f) => (f.member !== undefined && members.includes(f.member) ? [{ component: f.member, kind: f.kind, reason: f.reason }] : []));
263
+ if (!c.fallback) {
264
+ const named = new Set(drop.map((d) => d.component));
265
+ for (const m of members) {
266
+ if (!named.has(m) && !c.members.includes(m)) drop.push({ component: m, kind: "path", reason: `\`${m}\` deploys no root the composition could read` });
267
+ }
268
+ }
269
+ if (drop.length > 0) return { kind: drop[0]!.kind!, reason: drop[0]!.reason!, drop };
270
+ if (c.fallback) return { kind: c.fallback.kind, reason: c.fallback.reason };
271
+ return undefined;
272
+ }
273
+
274
+ /** `value` at the JSON Pointer `at`. */
275
+ function atPointer(value: unknown, at: string): unknown {
276
+ if (at === "") return value;
277
+ return at
278
+ .slice(1)
279
+ .split("/")
280
+ .map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"))
281
+ .reduce<unknown>((acc, key) => (acc == null ? acc : (acc as Record<string, unknown>)[key]), value);
282
+ }
283
+
284
+ /** Set `value` at the dotted `path` of `target`. */
285
+ function setPath(target: Record<string, unknown>, path: string, value: unknown): void {
286
+ const keys = path.split(".");
287
+ let at = target;
288
+ for (const k of keys.slice(0, -1)) {
289
+ const next = at[k];
290
+ at = (next && typeof next === "object" ? next : (at[k] = {})) as Record<string, unknown>;
291
+ }
292
+ at[keys[keys.length - 1]!] = value;
293
+ }
294
+
295
+ /**
296
+ * The outputs of components outside the group that the members' `vars`
297
+ * read, recovered from each member's input, which core resolved against
298
+ * them: `writeComposedRoot` turns those reads into literals.
299
+ */
300
+ export function outsideOutputs(members: readonly GroupMember<StepInput>[]): Record<string, Record<string, unknown>> {
301
+ const inGroup = new Set(members.map((m) => m.component));
302
+ const out: Record<string, Record<string, unknown>> = {};
303
+ for (const m of members) {
304
+ for (const r of wiringReferencesAt(m.step)) {
305
+ if (inGroup.has(r.source)) continue;
306
+ const value = atPointer(m.input, r.at);
307
+ if (value !== undefined) setPath((out[r.source] ??= {}), r.output, value);
308
+ }
309
+ }
310
+ return out;
311
+ }
312
+
313
+ /** The members' unresolved reads of components outside the group, by member. */
314
+ function unresolvedOf(members: readonly GroupMember<StepInput>[]): Map<string, string[]> {
315
+ return new Map(members.flatMap((m) => (m.ctx.unresolvedInputs?.length ? [[m.component, [...m.ctx.unresolvedInputs].sort()] as const] : [])));
316
+ }
317
+
318
+ /**
319
+ * The group while a member reads an output of a component outside it that
320
+ * is not known yet: no member is planned, each gets a hole naming what the
321
+ * group waits on, and core plans the group again once those apply.
322
+ */
323
+ function waitingGroup(ctx: GroupContext, members: readonly GroupMember<StepInput>[], unresolved: ReadonlyMap<string, string[]>): CapabilityGroupPlan {
324
+ const waits = [...new Set([...unresolved.values()].flat())].sort();
325
+ const by = [...unresolved.keys()].sort();
326
+ const plans: Record<string, CapabilityPlan> = {};
327
+ for (const m of members) {
328
+ const root = m.step.root as string;
329
+ const vars = Object.fromEntries(Object.entries((m.input?.vars ?? {}) as Record<string, unknown>).map(([k, v]) => [k, v === undefined ? null : v]));
330
+ const planDigest = computePlanDigest("terraform-apply-group-unresolved", { env: ctx.env, root, vars, unresolved: waits });
331
+ const reason =
332
+ by.length === 1 && by[0] === m.component
333
+ ? `inputs from ${waits.join(", ")}; planned again once they apply`
334
+ : `inputs from ${waits.join(", ")}, read by ${by.join(", ")} in its group; planned again once they apply`;
335
+ const changeSet: ChangeSetPart = {
336
+ member: { member: root, lexicon: "terraform", planner: "chant", status: "planned", planDigest, holes: [{ address: root, type: "terraform_root", reason }] },
337
+ entries: [],
338
+ };
339
+ plans[m.component] = { member: root, planDigest, changeSet };
340
+ }
341
+ return { members: plans, digest: computePlanDigest("terraform-apply-group-unresolved", { components: ctx.components, unresolved: waits }) };
342
+ }
343
+
344
+ /** A composed plan that no longer matches the one approved. Core's moved-plan mark, so nothing applies and the command exits `CHANT_EXIT.moved`, 4 (#4553). */
345
+ export class ComposedPlanMovedError extends Error {
346
+ readonly planMoved = true;
347
+ constructor(message: string) {
348
+ super(message);
349
+ this.name = "ComposedPlanMovedError";
350
+ }
351
+ }
352
+
353
+ /** The group methods, with `deps` in place of the defaults. */
354
+ export function composeGroup(deps: ComposeGroupDeps = {}): ComposeGroupMethods {
355
+ const project = deps.project ?? loadProject;
356
+ const exec = deps.exec ?? runBinary;
357
+ const envOf = (): NodeJS.ProcessEnv => deps.env ?? process.env;
358
+ const log = deps.log ?? ((line: string) => console.error(line));
359
+ const readOutputs = deps.outputs ?? ((root: string) => terraformOutputs({ root }));
360
+ const identities = deps.identity ?? declaredTargetCloudIdentity;
361
+
362
+ /** The composition of `members` as `groupFallback` judges it, with each member's identity. */
363
+ async function compositionOf(ctx: GroupContext, p: ComposeProject, members: readonly GroupMember<StepInput>[]): Promise<Composition> {
364
+ const lookup = await identities(p.projectRoot, ctx.env);
365
+ const c = groupComposition(p, members, envOf(), memberIdentity(p, members, lookup));
366
+ if (!c) throw new Error(composeOff(p.terraform)?.reason ?? "`terraform.compose` is not enabled");
367
+ return c;
368
+ }
369
+
370
+ /** The composition the group plans as, or throws when it does not compose. */
371
+ async function composed(ctx: GroupContext, members: readonly GroupMember<StepInput>[]): Promise<{ p: ComposeProject; c: Composition }> {
372
+ const p = await project();
373
+ const off = composeOff(p.terraform);
374
+ if (off) throw new Error(off.reason);
375
+ const c = await compositionOf(ctx, p, members);
376
+ const refused = groupFallbackOf(c, members.map((m) => m.component));
377
+ if (refused) throw new Error(`the group no longer composes: ${refused.reason}`);
378
+ return { p, c };
379
+ }
380
+
381
+ /** Build a runner, run `fn` on it, and release it and its directory whatever happened. */
382
+ async function withRunner<T>(p: ComposeProject, c: Composition, members: readonly GroupMember<StepInput>[], lock: boolean, fn: (r: ComposedRunner) => Promise<T>): Promise<T> {
383
+ const work = mkdtempSync(join(tmpdir(), "chant-compose-"));
384
+ const o: GroupRunnerOptions = {
385
+ ...p,
386
+ composition: c,
387
+ lock,
388
+ vars: Object.fromEntries(members.map((m) => [m.component, m.step.vars as Record<string, unknown> | undefined])),
389
+ componentOutputs: outsideOutputs(members),
390
+ env: envOf(),
391
+ work,
392
+ };
393
+ const runner = deps.runner ? deps.runner(o) : defaultRunner(o, log);
394
+ try {
395
+ return await fn(runner);
396
+ } finally {
397
+ try {
398
+ await runner.release();
399
+ } finally {
400
+ rmSync(work, { recursive: true, force: true });
401
+ }
402
+ }
403
+ }
404
+
405
+ return {
406
+ async groupReads(_ctx, members) {
407
+ const p = await project();
408
+ if (composeOff(p.terraform)) return [];
409
+ return hclGroupReads(p, members);
410
+ },
411
+
412
+ async groupFallback(ctx, members) {
413
+ const p = await project();
414
+ const off = composeOff(p.terraform);
415
+ if (off) return { kind: off.kind, reason: off.reason };
416
+ const c = await compositionOf(ctx, p, members);
417
+ // A member left out is dropped alone; core asks again with the rest, and the estate check runs then.
418
+ const refused = groupFallbackOf(c, members.map((m) => m.component));
419
+ if (refused) return refused;
420
+ if (c.estate) {
421
+ const binary = p.terraform.binary ?? "choudoufu";
422
+ const v = await exec(binary, ["version"], p.projectRoot, envOf());
423
+ // A binary that cannot say its version is left to the plan, which says why it fails.
424
+ const old = v.code === 0 ? estateVersionFallback(v.stdout) : undefined;
425
+ if (old) return { kind: old.kind, reason: old.reason };
426
+ }
427
+ return undefined;
428
+ },
429
+
430
+ async planGroup(ctx, members) {
431
+ const unresolved = unresolvedOf(members);
432
+ if (unresolved.size > 0) return waitingGroup(ctx, members, unresolved);
433
+ const { p, c } = await composed(ctx, members);
434
+ // Planned without locks and released at once: nothing stays held or changed if core refuses the plan or makes it wait.
435
+ const planned = await withRunner(p, c, members, false, (r) => r.plan());
436
+ if (planned.fallback) throw new Error(`the group no longer composes: ${planned.fallback.reason}`);
437
+ if (planned.error) throw new Error(planned.error);
438
+ const plans: Record<string, CapabilityPlan> = {};
439
+ for (const m of members) {
440
+ const part = planned.members[m.component];
441
+ if (!part) throw new Error(`the composed plan has no part for ${m.component}`);
442
+ plans[m.component] = { member: part.member, planDigest: part.planDigest, changeSet: part.changeSet, outputs: part.outputs, shape: part.shape };
443
+ }
444
+ const artifact: ComposedArtifact = { digest: planned.digest, estate: c.estate === true };
445
+ return { members: plans, digest: planned.digest, artifact };
446
+ },
447
+
448
+ async runGroup(ctx, run, members) {
449
+ if (unresolvedOf(members).size > 0) throw new Error("the group reads outputs of components outside it that are not known yet, so it cannot apply");
450
+ const approved = run.artifact as ComposedArtifact | undefined;
451
+ if (typeof approved?.digest !== "string" || approved.digest === "") throw new Error("no composed plan of the group to apply");
452
+ const { p, c } = await composed(ctx, members);
453
+ const notStarted = (error: string): CapabilityGroupOutcome => ({
454
+ members: Object.fromEntries(members.map((m) => [m.component, { status: "not-started" as const, error }])),
455
+ error,
456
+ });
457
+ const applied = await withRunner(p, c, members, true, async (r) => {
458
+ // The approved plan was made without locks; plan again under them, and apply only that same plan.
459
+ const planned = await r.plan();
460
+ if (planned.fallback) return notStarted(`the group no longer composes: ${planned.fallback.reason}`);
461
+ if (planned.error) return notStarted(planned.error);
462
+ if (planned.digest !== approved.digest) {
463
+ throw new ComposedPlanMovedError(`the composed plan moved since it was approved (approved ${approved.digest}, planned now ${planned.digest}), so nothing applied`);
464
+ }
465
+ return r.apply();
466
+ });
467
+ const out: Record<string, GroupMemberOutcome> = {};
468
+ for (const m of members) {
469
+ const o = applied.members[m.component] as (GroupMemberOutcome & { lockHeld?: true; kept?: string }) | undefined;
470
+ if (!o) {
471
+ out[m.component] = { status: "failed", error: applied.error ?? `the composed apply said nothing of ${m.component}` };
472
+ continue;
473
+ }
474
+ let outputs = o.outputs;
475
+ // A root's state is written by now; read its outputs from there when its part of the working state could not be shown.
476
+ if (!outputs && !c.estate && (o.status === "applied" || o.status === "no-changes")) {
477
+ outputs = await readOutputs(m.step.root as string).catch(() => undefined);
478
+ }
479
+ const held = o.lockHeld ? `its state was kept unwritten${o.kept ? ` at ${o.kept}` : ""}, and its lock stays held until a later run writes it` : undefined;
480
+ const error = [o.error, held].filter(Boolean).join("; ");
481
+ out[m.component] = { status: o.status, ...(outputs ? { outputs } : {}), ...(error ? { error } : {}) };
482
+ }
483
+ return { members: out, ...(applied.error ? { error: applied.error } : {}) };
484
+ },
485
+ };
486
+ }
487
+
488
+ /** The group methods `terraform-apply` uses. */
489
+ export const terraformComposeGroup: ComposeGroupMethods = composeGroup();