@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
package/src/cone.ts ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * choudoufu's cone plans (#4545, moved from terragucci's `cone.ts`): `plan
3
+ * -cone=<base>` plans only the blocks a change touches and every block that
4
+ * reads them.
5
+ *
6
+ * A wave that applies an open pull request plans a live root as the cone of
7
+ * its change since the pull request's base, as the plan note did. A wave
8
+ * that applies a merge commit and a drift run plan the whole estate, as do
9
+ * a root with no `live` block, an estate file (`estate.chdf.hcl`), whose
10
+ * form choudoufu refuses `-cone` on, and any binary but choudoufu.
11
+ *
12
+ * choudoufu's guard can plan the whole estate anyway (no base, a provider,
13
+ * terraform or live block or the lock file changed, a cone over 30% of the
14
+ * estate's blocks, and so on). It says why in a warning. A cone plan says
15
+ * its size in a warning too, and with `-out FILE` writes `FILE.cone.json`:
16
+ * the cone's base, the commit it named and the addresses it targets.
17
+ *
18
+ * The record's targets join the root's plan digest ({@link conePlanDigest}),
19
+ * so the digest an approval binds names the cone. A wave that plans again
20
+ * after its approval and finds another cone has another digest, and refuses
21
+ * as moved. choudoufu's `apply FILE` checks the same record again.
22
+ *
23
+ * Pure, apart from reading the record beside a plan file.
24
+ */
25
+ import { existsSync, readFileSync } from "node:fs";
26
+ import { basename } from "node:path";
27
+ import { computePlanDigest } from "@intentius/chant/lifecycle/plan-digest";
28
+
29
+ /** The flags choudoufu refuses beside `-cone`, or plans in full with: a plan that carries one gets no `-cone`. */
30
+ export const CONE_EXCLUDES = ["-target", "-exclude", "-destroy", "-refresh-only", "-json", "-estate"] as const;
31
+
32
+ /** What choudoufu writes beside a saved cone plan, `FILE.cone.json`. */
33
+ export interface ConeRecord {
34
+ base: string;
35
+ base_sha: string;
36
+ targets: string[];
37
+ }
38
+
39
+ /** What one root's cone plan did: planned the cone, or planned the whole estate for the guard's reason. */
40
+ export interface PlanCone {
41
+ /** The revision the plan was asked to measure the change from. */
42
+ base: string;
43
+ /** With a cone: its blocks, and the estate's, as choudoufu counted them. Absent when it did not say. */
44
+ blocks?: number;
45
+ of?: number;
46
+ /** With a cone: the record beside the plan file. */
47
+ record?: ConeRecord;
48
+ /** The guard planned the whole estate: its kind and why. */
49
+ fallback?: { kind: string; reason: string };
50
+ }
51
+
52
+ /** Whether `binary` (a name or a path) is choudoufu. */
53
+ export const isChoudoufu = (binary: string): boolean => basename(binary) === "choudoufu";
54
+
55
+ /**
56
+ * The `-cone` argument a plan takes: `-cone=<base>` for choudoufu with a
57
+ * base, on a root that takes it (`live`), when none of `args` is a flag in
58
+ * {@link CONE_EXCLUDES}. Otherwise none, and the plan is full.
59
+ */
60
+ export function coneArgs(binary: string, base: string | undefined, args: readonly string[], cone: boolean): string[] {
61
+ if (!base || !cone || !isChoudoufu(binary)) return [];
62
+ const excluded = args.some((a) => CONE_EXCLUDES.some((f) => a === f || a.startsWith(`${f}=`)));
63
+ return excluded ? [] : [`-cone=${base}`];
64
+ }
65
+
66
+ /** The path of the cone record beside a plan file. */
67
+ export const coneRecordPath = (planFile: string): string => `${planFile}.cone.json`;
68
+
69
+ /** The cone record beside `planFile`, or none when there is none or it does not read as one. */
70
+ export function readConeRecord(planFile: string): ConeRecord | undefined {
71
+ const path = coneRecordPath(planFile);
72
+ if (!existsSync(path)) return undefined;
73
+ try {
74
+ const r = JSON.parse(readFileSync(path, "utf-8")) as Partial<ConeRecord> | null;
75
+ if (!r || typeof r.base !== "string" || !Array.isArray(r.targets) || !r.targets.every((t) => typeof t === "string")) return undefined;
76
+ return { base: r.base, base_sha: typeof r.base_sha === "string" ? r.base_sha : "", targets: [...r.targets].sort() };
77
+ } catch {
78
+ return undefined;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * The detail of the last warning with this summary in a plan's output, its
84
+ * lines joined. The binary prints a warning as `Warning: <summary>`, a blank
85
+ * line, and the detail up to the next blank line or the plan's summary; a box
86
+ * drawn around it with colour on is stripped.
87
+ */
88
+ function warningDetail(text: string, summary: string): string | undefined {
89
+ const lines = text.replace(/\r\n/g, "\n").split("\n").map((l) => l.replace(/^[\s│╷╵]*/, "").trimEnd());
90
+ let found: string | undefined;
91
+ for (let i = 0; i < lines.length; i++) {
92
+ if (lines[i] !== `Warning: ${summary}`) continue;
93
+ let j = i + 1;
94
+ while (j < lines.length && lines[j] === "") j++;
95
+ const detail: string[] = [];
96
+ for (; j < lines.length && lines[j] !== "" && !/^(Plan: |No changes\.|Changes to Outputs:)/.test(lines[j]!); j++) detail.push(lines[j]!);
97
+ found = detail.join(" ").replace(/\s+/g, " ").trim();
98
+ }
99
+ return found;
100
+ }
101
+
102
+ /**
103
+ * What a plan run with `-cone=<base>` did, from its output and the record
104
+ * beside `planFile`. A fallback warning means the whole estate was planned,
105
+ * even after a cone warning. With no fallback, the record is the cone. With
106
+ * neither, choudoufu said nothing this reads, and the plan counts as full.
107
+ */
108
+ export function readPlanCone(output: string, planFile: string, base: string): PlanCone {
109
+ const fell = warningDetail(output, "Cone plan fell back to a full plan");
110
+ if (fell !== undefined) {
111
+ const m = /^This plan covers the whole estate \(guard ([^)]*)\): (.*?)\.?$/.exec(fell);
112
+ return { base, fallback: m ? { kind: m[1]!, reason: m[2]! } : { kind: "unknown", reason: fell } };
113
+ }
114
+ const record = readConeRecord(planFile);
115
+ if (!record) return { base, fallback: { kind: "unknown", reason: "choudoufu wrote no cone record beside the plan" } };
116
+ const size = /: (\d+) of the estate's (\d+) blocks/.exec(warningDetail(output, "Cone plan") ?? "");
117
+ return { base, ...(size ? { blocks: Number(size[1]), of: Number(size[2]) } : {}), record };
118
+ }
119
+
120
+ /** What a cone plan did, in words: its size, or why it planned the whole estate. */
121
+ export function coneText(cone: PlanCone): string {
122
+ if (cone.fallback) return `the whole estate, since ${cone.fallback.reason} (guard ${cone.fallback.kind})`;
123
+ const n = cone.record?.targets.length ?? 0;
124
+ const size = cone.blocks !== undefined && cone.of !== undefined ? `${cone.blocks} of the estate's ${cone.of} blocks` : `${n} target${n === 1 ? "" : "s"}`;
125
+ return `the cone of the change since ${cone.base}, ${size}`;
126
+ }
127
+
128
+ /**
129
+ * A root's plan digest with its cone in it: the plan's own digest, unchanged,
130
+ * when the plan was full; with a cone, the digest over the plan's digest and
131
+ * the cone's targets, so an approval of one cone counts for no other. The
132
+ * digest's kind is terragucci's, so approvals recorded before the move still
133
+ * name the same digest.
134
+ */
135
+ export function conePlanDigest(planDigest: string, cone: PlanCone | undefined): string {
136
+ if (!cone?.record) return planDigest;
137
+ return computePlanDigest("terragucci-cone-plan", { plan: planDigest, targets: [...cone.record.targets].sort() });
138
+ }
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Every file lister in the lexicon reads the same configuration files for one
3
+ * root, through `./hcl/files.ts` (chant#4457): `.tf` and `.tf.json`, plus
4
+ * `.tofu` and `.tofu.json` when the root runs tofu or choudoufu, with `x.tofu`
5
+ * shadowing `x.tf`. One test per lister that changed. The fast `live` readers
6
+ * also read a `.tf.json` file's `live` block (chant#4504).
7
+ */
8
+ import { execFileSync } from "node:child_process";
9
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ import { afterEach, describe, expect, test } from "vitest";
13
+ import { loadHcl2json } from "@intentius/chant/terraform/hcl2json";
14
+ import { listConfigFiles, readsTofuFiles, selectConfigFiles } from "./hcl/files";
15
+ import { configFiles, liveEstate, localModules, rootState } from "./compose/hcl";
16
+ import { hasCloudBlock, stateObject } from "./state/backend";
17
+ import { estateOfDir } from "./report";
18
+ import { detectLiveEstate, detectLivePolicyVerbs } from "./op/activities/live-detect";
19
+ import { rootWatchPaths } from "./changed-roots";
20
+ import { editPins } from "./pin/edit";
21
+ import { runPinRollout } from "./pin/rollout";
22
+ import { terragruntAffected } from "./terragrunt/affected";
23
+
24
+ const made: string[] = [];
25
+ afterEach(() => {
26
+ for (const d of made.splice(0)) rmSync(d, { recursive: true, force: true });
27
+ });
28
+
29
+ /** A temporary directory holding `files`, by path relative to it. */
30
+ function tree(files: Record<string, string>): string {
31
+ const dir = mkdtempSync(join(tmpdir(), "chant-4457-"));
32
+ made.push(dir);
33
+ for (const [path, text] of Object.entries(files)) {
34
+ mkdirSync(join(dir, path, ".."), { recursive: true });
35
+ writeFileSync(join(dir, path), text);
36
+ }
37
+ return dir;
38
+ }
39
+
40
+ describe("hcl/files: the one lister", () => {
41
+ test("which binaries read .tofu files", () => {
42
+ expect(readsTofuFiles(undefined)).toBe(false);
43
+ expect(readsTofuFiles("terraform")).toBe(false);
44
+ expect(readsTofuFiles("/opt/bin/terraform")).toBe(false);
45
+ expect(readsTofuFiles("tofu")).toBe(true);
46
+ expect(readsTofuFiles("/usr/local/bin/tofu")).toBe(true);
47
+ expect(readsTofuFiles("choudoufu")).toBe(true);
48
+ });
49
+
50
+ test("the file set follows the binary, and x.tofu shadows x.tf", () => {
51
+ const names = ["main.tf", "main.tofu", "extra.tf.json", "extra.tofu.json", "vars.tf", "notes.md", "terraform.tfvars", "estate.chdf.hcl"];
52
+ expect(selectConfigFiles(names)).toEqual(["extra.tf.json", "main.tf", "vars.tf"]);
53
+ expect(selectConfigFiles(names, "terraform")).toEqual(["extra.tf.json", "main.tf", "vars.tf"]);
54
+ expect(selectConfigFiles(names, "tofu")).toEqual(["extra.tofu.json", "main.tofu", "vars.tf"]);
55
+ expect(selectConfigFiles(names, "choudoufu")).toEqual(["extra.tofu.json", "main.tofu", "vars.tf"]);
56
+ });
57
+
58
+ test("a directory is listed by its files only, not a directory named like one", () => {
59
+ const dir = tree({ "main.tf": "", "a.tofu": "", "odd.tf/inner.txt": "" });
60
+ expect(listConfigFiles(dir)).toEqual(["main.tf"]);
61
+ expect(listConfigFiles(dir, "tofu")).toEqual(["a.tofu", "main.tf"]);
62
+ });
63
+ });
64
+
65
+ describe("compose/hcl reads the lister's set", () => {
66
+ test("configFiles follows the binary", () => {
67
+ const dir = tree({ "main.tf": "", "main.tofu": "", "x.tf.json": "{}" });
68
+ expect(configFiles(dir).map((p) => p.slice(dir.length + 1))).toEqual(["main.tf", "x.tf.json"]);
69
+ expect(configFiles(dir, "tofu").map((p) => p.slice(dir.length + 1))).toEqual(["main.tofu", "x.tf.json"]);
70
+ });
71
+
72
+ test("a backend in a .tofu file is seen under tofu only", () => {
73
+ const dir = tree({ "backend.tofu": 'terraform {\n backend "gcs" {\n bucket = "b"\n prefix = "p"\n }\n}\n' });
74
+ expect(rootState(dir, "app", { env: {}, recorded: null }).own).toEqual({ key: "local:app/terraform.tfstate" });
75
+ expect(rootState(dir, "app", { env: {}, recorded: null, binary: "tofu" }).own).toEqual({ bucket: "b", key: "p" });
76
+ });
77
+
78
+ test("a live block in a .tofu file is a choudoufu root's estate", () => {
79
+ const dir = tree({ "main.tofu": 'terraform {\n live {\n estate = "prod"\n }\n}\n' });
80
+ expect(liveEstate(dir)).toEqual({ live: true, estate: "prod" });
81
+ expect(liveEstate(dir, "terraform")).toEqual({ live: false });
82
+ });
83
+
84
+ test("a local module called from a .tofu file is followed under tofu", () => {
85
+ const dir = tree({ "root/main.tofu": 'module "net" {\n source = "../net"\n}\n', "net/main.tf": "" });
86
+ expect(localModules(join(dir, "root"))).toEqual([]);
87
+ expect(localModules(join(dir, "root"), "tofu")).toEqual([join(dir, "net")]);
88
+ });
89
+ });
90
+
91
+ describe("state/backend reads the lister's set", () => {
92
+ test("a cloud block in a .tofu file is refused under tofu, and unseen under terraform", () => {
93
+ const dir = tree({ "main.tofu": 'terraform {\n cloud {\n organization = "o"\n }\n}\n' });
94
+ expect(hasCloudBlock(dir)).toBe(false);
95
+ expect(hasCloudBlock(dir, "tofu")).toBe(true);
96
+ expect(hasCloudBlock(dir, "choudoufu")).toBe(true);
97
+ expect(stateObject(dir, {}, undefined, "tofu")).toMatchObject({ refused: "cloud" });
98
+ expect(stateObject(dir, {}, undefined, "terraform")).toMatchObject({ backend: "local" });
99
+ });
100
+
101
+ test("a cloud block in a .tofu.json file is seen under tofu", () => {
102
+ const dir = tree({ "main.tofu.json": JSON.stringify({ terraform: { cloud: { organization: "o" } } }) });
103
+ expect(hasCloudBlock(dir, "tofu")).toBe(true);
104
+ });
105
+ });
106
+
107
+ describe("report and live-detect read choudoufu's set", () => {
108
+ const live = 'terraform {\n live {\n estate = "prod"\n }\n}\n';
109
+
110
+ test("estateOfDir finds a live block in a .tofu file", () => {
111
+ expect(estateOfDir(tree({ "main.tofu": live }))).toBe("prod");
112
+ expect(estateOfDir(tree({ "main.tofu": live }), "terraform")).toBeUndefined();
113
+ });
114
+
115
+ test("detectLiveEstate finds a live block in a .tofu file, and x.tofu shadows x.tf", () => {
116
+ expect(detectLiveEstate(tree({ "main.tofu": live }))).toBe("prod");
117
+ expect(detectLiveEstate(tree({ "main.tf": live, "main.tofu": "" }))).toBeUndefined();
118
+ expect(detectLiveEstate(tree({ "main.tf": live, "main.tofu": "" }), "terraform")).toBe("prod");
119
+ });
120
+
121
+ const liveJson = JSON.stringify({ terraform: { required_version: ">= 1.5.0", live: { estate: "prod" } } });
122
+
123
+ test("estateOfDir finds a live block in a .tf.json file (chant#4504)", () => {
124
+ expect(estateOfDir(tree({ "main.tf.json": liveJson }))).toBe("prod");
125
+ expect(estateOfDir(tree({ "main.tf.json": liveJson }), "terraform")).toBe("prod");
126
+ // JSON syntax may write the terraform block as an array of bodies.
127
+ expect(estateOfDir(tree({ "main.tf.json": JSON.stringify({ terraform: [{}, { live: [{ estate: "second" }] }] }) }))).toBe("second");
128
+ expect(estateOfDir(tree({ "main.tf.json": JSON.stringify({ terraform: { live: { estate: "" } } }) }))).toBeUndefined();
129
+ expect(estateOfDir(tree({ "main.tf.json": "{ not json" }))).toBeUndefined();
130
+ // A live key outside the terraform block is not a live block.
131
+ expect(estateOfDir(tree({ "main.tf.json": JSON.stringify({ live: { estate: "prod" } }) }))).toBeUndefined();
132
+ });
133
+
134
+ test("estateOfDir takes the first live block in filename order across .tf and .tf.json", () => {
135
+ expect(estateOfDir(tree({ "a.tf.json": liveJson, "b.tf": live.replace("prod", "other") }))).toBe("prod");
136
+ expect(estateOfDir(tree({ "a.tf": live.replace("prod", "other"), "b.tf.json": liveJson }))).toBe("other");
137
+ });
138
+
139
+ test("estateOfDir reads a .tofu.json live block under choudoufu, and x.tofu.json shadows x.tf.json", () => {
140
+ expect(estateOfDir(tree({ "main.tofu.json": liveJson }))).toBe("prod");
141
+ expect(estateOfDir(tree({ "main.tofu.json": liveJson }), "terraform")).toBeUndefined();
142
+ expect(estateOfDir(tree({ "main.tf.json": liveJson, "main.tofu.json": "{}" }))).toBeUndefined();
143
+ });
144
+
145
+ test("detectLiveEstate finds a live block in a .tf.json file (chant#4504)", () => {
146
+ expect(detectLiveEstate(tree({ "main.tf.json": liveJson }))).toBe("prod");
147
+ expect(detectLiveEstate(tree({ "main.tf.json": liveJson }), "terraform")).toBe("prod");
148
+ expect(detectLiveEstate(tree({ "main.tofu.json": liveJson }))).toBe("prod");
149
+ expect(detectLiveEstate(tree({ "main.tf.json": JSON.stringify({ terraform: { live: { estate: "" } } }) }))).toBeUndefined();
150
+ expect(detectLiveEstate(tree({ "main.tf.json": "{ not json" }))).toBeUndefined();
151
+ // The sidecar still comes first.
152
+ expect(detectLiveEstate(tree({ "estate.chdf.hcl": 'estate = "side"\n', "main.tf.json": liveJson }))).toBe("side");
153
+ });
154
+
155
+ test("detectLivePolicyVerbs reads a .tf.json live block's policy, as an object or an array", () => {
156
+ const policy = { undeclared_tagged: "adopt", undeclared_untagged: "report" };
157
+ expect(detectLivePolicyVerbs(tree({ "main.tf.json": JSON.stringify({ terraform: { live: { estate: "prod", policy } } }) }))).toEqual({
158
+ undeclaredTagged: "adopt",
159
+ undeclaredUntagged: "report",
160
+ });
161
+ expect(detectLivePolicyVerbs(tree({ "main.tf.json": JSON.stringify({ terraform: { live: { estate: "prod", policy: [{ undeclared_untagged: "delete" }] } } }) }))).toEqual({
162
+ undeclaredTagged: undefined,
163
+ undeclaredUntagged: "delete",
164
+ });
165
+ expect(detectLivePolicyVerbs(tree({ "main.tf.json": liveJson }))).toBeUndefined();
166
+ });
167
+ });
168
+
169
+ describe("pin rollout's listers follow the binary (chant#4504)", () => {
170
+ const REGISTRY = "app.terraform.io/acme/vpc/aws";
171
+ const call = (version: string) => `module "vpc" {\n source = "${REGISTRY}"\n version = "${version}"\n}\n`;
172
+
173
+ /** A git repository on `main` holding `files`, committed. */
174
+ function repo(files: Record<string, string>): string {
175
+ const dir = tree(files);
176
+ const git = (...args: string[]) =>
177
+ execFileSync("git", args, { cwd: dir, env: { ...process.env, GIT_AUTHOR_NAME: "t", GIT_AUTHOR_EMAIL: "t@t", GIT_COMMITTER_NAME: "t", GIT_COMMITTER_EMAIL: "t@t" } });
178
+ git("init", "--quiet", "--initial-branch=main");
179
+ git("add", ".");
180
+ git("commit", "--quiet", "-m", "fixture");
181
+ return dir;
182
+ }
183
+
184
+ async function roots(cwd: string, binary?: string) {
185
+ const r = await runPinRollout({ cwd, module: REGISTRY, from: "1.3.0", to: "1.4.0", parser: await loadHcl2json(), mode: "report", ...(binary ? { binary } : {}) });
186
+ return Object.fromEntries(r.roots.map((x) => [x.root, { state: x.state, files: x.files }]));
187
+ }
188
+
189
+ test("candidateRoots and rootFiles read .tofu files under tofu only, with x.tofu shadowing x.tf", async () => {
190
+ const cwd = repo({
191
+ "only-tofu/main.tofu": call("1.3.0"),
192
+ "shadowed/main.tf": call("1.2.0"),
193
+ "shadowed/main.tofu": call("1.3.0"),
194
+ "plain/main.tf": call("1.3.0"),
195
+ "plain/notes.md": call("1.3.0"),
196
+ });
197
+ expect(await roots(cwd)).toEqual({
198
+ plain: { state: "from", files: ["plain/main.tf"] },
199
+ shadowed: { state: "elsewhere", files: ["shadowed/main.tf"] },
200
+ });
201
+ expect(await roots(cwd, "tofu")).toEqual({
202
+ "only-tofu": { state: "from", files: ["only-tofu/main.tofu"] },
203
+ plain: { state: "from", files: ["plain/main.tf"] },
204
+ shadowed: { state: "from", files: ["shadowed/main.tofu"] },
205
+ });
206
+ });
207
+
208
+ test("a .tf.json root is a candidate, and its moving call is refused by name", async () => {
209
+ const cwd = repo({ "json/main.tf.json": JSON.stringify({ module: { vpc: { source: REGISTRY, version: "1.3.0" } } }) });
210
+ const r = await runPinRollout({ cwd, module: REGISTRY, from: "1.3.0", to: "1.4.0", parser: await loadHcl2json(), mode: "report" });
211
+ expect(r.roots).toEqual([expect.objectContaining({ root: "json", state: "refused", files: ["json/main.tf.json"], reason: expect.stringContaining("JSON syntax") })]);
212
+ expect(r.waves).toEqual([]);
213
+ });
214
+ });
215
+
216
+ describe("terragrunt affected follows a module's .tofu calls (chant#4504)", () => {
217
+ const unit = { path: "live/app", dependencies: [], reading: ["live/app/terragrunt.hcl", "modules/app/main.tofu"] };
218
+
219
+ test("a change under a module called from a .tofu file is a module-call reason", () => {
220
+ const dir = tree({
221
+ "live/app/terragrunt.hcl": 'terraform {\n source = "../../modules/app"\n}\n',
222
+ "modules/app/main.tofu": 'module "net" {\n source = "../net"\n}\n',
223
+ "modules/net/main.tf": "",
224
+ });
225
+ expect(terragruntAffected({ dir, units: [unit], terragruntSelected: [], changedFiles: ["modules/net/main.tf"] })).toEqual({
226
+ units: [{ path: "live/app", reasons: [{ kind: "module-call", files: ["modules/net/main.tf"], via: "modules/net" }] }],
227
+ notes: [],
228
+ });
229
+ });
230
+
231
+ test("a call in an x.tf that x.tofu shadows is not followed", () => {
232
+ const dir = tree({
233
+ "live/app/terragrunt.hcl": 'terraform {\n source = "../../modules/app"\n}\n',
234
+ "modules/app/main.tofu": "",
235
+ "modules/app/main.tf": 'module "old" {\n source = "../old"\n}\n',
236
+ "modules/old/main.tf": "",
237
+ });
238
+ expect(terragruntAffected({ dir, units: [unit], terragruntSelected: [], changedFiles: ["modules/old/main.tf"] })).toEqual({ units: [], notes: [] });
239
+ });
240
+ });
241
+
242
+ describe("changed-roots reads the lister's set", () => {
243
+ test("a module called from a .tofu file is watched for a tofu root only", () => {
244
+ const dir = tree({ "app/main.tofu": 'module "net" {\n source = "../modules/net"\n}\n', "modules/net/main.tf": "" });
245
+ expect(rootWatchPaths(dir, { dir: "app" })).toEqual(["app"]);
246
+ expect(rootWatchPaths(dir, { dir: "app" }, "tofu")).toEqual(["app", "modules/net"]);
247
+ });
248
+ });
249
+
250
+ describe("pin edit on a JSON-syntax file", () => {
251
+ const REGISTRY = "app.terraform.io/acme/vpc/aws";
252
+
253
+ test("a call that would move is refused by name, and the file is left as written", async () => {
254
+ const text = JSON.stringify({ module: { vpc: { source: REGISTRY, version: "1.3.0" } } });
255
+ const result = await editPins(text, "main.tf.json", { module: REGISTRY, from: "1.3.0", to: "1.4.0" }, await loadHcl2json());
256
+ expect(result.content).toBe(text);
257
+ expect(result.calls).toEqual([expect.objectContaining({ outcome: "refused", file: "main.tf.json", call: "module.vpc" })]);
258
+ });
259
+
260
+ test("a call already at the new pin reads as already", async () => {
261
+ const text = JSON.stringify({ module: { vpc: { source: REGISTRY, version: "1.4.0" } } });
262
+ const result = await editPins(text, "main.tofu.json", { module: REGISTRY, from: "1.3.0", to: "1.4.0" }, await loadHcl2json());
263
+ expect(result.calls.map((c) => c.outcome)).toEqual(["already"]);
264
+ });
265
+ });
package/src/config.ts CHANGED
@@ -63,8 +63,15 @@ export const terraformRootSchema = z.strictObject({
63
63
  workspace: z.string().optional(),
64
64
  /** `-var-file` arguments, in order, relative to `dir`. */
65
65
  varFiles: z.array(z.string()).optional(),
66
- /** `-backend-config` key/value pairs handed to `init`. */
66
+ /** `-backend-config` key/value pairs handed to `init`, after {@link backendConfigFiles}. */
67
67
  backendConfig: z.record(z.string(), z.string()).optional(),
68
+ /**
69
+ * Backend-config files handed to `init` as `-backend-config=<path>`, in
70
+ * order and before the {@link backendConfig} pairs, relative to `dir`
71
+ * (chant#4459). The composition reads their `key = "value"` lines as it
72
+ * reads the pairs, to find a root's state before init.
73
+ */
74
+ backendConfigFiles: z.array(z.string()).optional(),
68
75
  /**
69
76
  * The choudoufu estate this root runs against, for a live root whose HCL
70
77
  * does not declare one of its own (#2479).
@@ -109,6 +116,16 @@ export const terraformRootSchema = z.strictObject({
109
116
  * every root, for one run.
110
117
  */
111
118
  observe: terraformObserveDepthSchema.optional(),
119
+ /**
120
+ * The root is declared only so a change to it brings this project into the
121
+ * change (#4484). A dns member that declares another cloud's root, so that
122
+ * an azure change reaches dns, sets it on that root. Change detection
123
+ * (`changedUnits`) still counts the root; the build and every live read
124
+ * skip it, so it is never drawn or read as this project's own: its entities
125
+ * are not built, and `describeResources`, drift and the owned-resource
126
+ * reads leave it out. An Op step that names the root still runs against it.
127
+ */
128
+ signalOnly: z.boolean().optional(),
112
129
  });
113
130
 
114
131
  export const terraformConfigSchema = z.strictObject({
@@ -145,19 +162,37 @@ export const terraformConfigSchema = z.strictObject({
145
162
  moduleRoot: z.string().optional(),
146
163
  /** The default {@link TerraformRootConfig.observe} for every root that sets none (#4358). */
147
164
  observe: terraformObserveDepthSchema.optional(),
148
- /** Named root modules. The name is the entity-key prefix, so keep it stable. */
149
- roots: z.record(z.string(), terraformRootSchema).default({}),
150
165
  /**
151
- * One pulumi workspace member read into the graph (#3179). `chant workspace
152
- * graph` writes this into the reader project it makes for a `pulumi` member;
153
- * it is not meant to be written by hand. `dir` is the member's directory,
154
- * absolute; `label` names the entities. The graph is the checked-in
155
- * `stack-export.json` there; the program is never run.
166
+ * Composed runs (#4419, #4438), off unless `enabled` is true. When on, the
167
+ * `terraform-apply` components a change reaches, and the ones they read,
168
+ * plan as one plan and apply as one apply of a composed root of `module`
169
+ * calls, so a value one root creates reaches its readers in that apply. A
170
+ * component that cannot compose plans wave by wave as without the setting,
171
+ * and the report says why (`compositionFor` in `compose.ts`).
172
+ *
173
+ * `max` caps how many roots compose; a change over it applies wholly in
174
+ * waves. Default 10.
156
175
  */
157
- pulumiMember: z.strictObject({ dir: z.string().min(1), label: z.string().optional() }).optional(),
176
+ compose: z
177
+ .strictObject({
178
+ enabled: z.boolean(),
179
+ max: z.number().int().min(2).optional(),
180
+ })
181
+ .optional(),
182
+ /** Named root modules. The name is the entity-key prefix, so keep it stable. */
183
+ roots: z.record(z.string(), terraformRootSchema).default({}),
158
184
  });
159
185
 
160
186
  export type TerraformRootConfig = z.infer<typeof terraformRootSchema>;
187
+
188
+ /**
189
+ * The roots a project owns: `roots` without the ones marked `signalOnly`
190
+ * (#4484), which only feed change detection. The build and the live reads
191
+ * take these; `changedUnits` takes every root.
192
+ */
193
+ export function ownRoots<R extends { signalOnly?: boolean }>(roots: Record<string, R> | undefined): Record<string, R> {
194
+ return Object.fromEntries(Object.entries(roots ?? {}).filter(([, r]) => r.signalOnly !== true));
195
+ }
161
196
  export type TerraformConfig = z.infer<typeof terraformConfigSchema>;
162
197
 
163
198
  declare module "@intentius/chant/config" {
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The terraform lexicon's preset for an op wave's cost hold (#4545,
3
+ * `holds.cost` on the spec): infracost over each root's plan document, the
4
+ * `show -json` file `terraform-apply`'s plan names as `artifact.planJson`.
5
+ *
6
+ * ```ts
7
+ * const spec = { ..., step: { kind: "terraform-apply", root: "{target}" }, holds: { cost: terraformCostHold({ approveAbove: 100 }) } };
8
+ * ```
9
+ *
10
+ * The shape is core's `OpWaveCostHold`, written out here so the lexicon
11
+ * builds against a core without it.
12
+ */
13
+
14
+ /** infracost's own command over one plan document. */
15
+ export const INFRACOST_ARGV = ["infracost", "breakdown", "--path", "{planJson}", "--format", "json", "--log-level", "error"] as const;
16
+
17
+ export interface TerraformCostHold {
18
+ command: string[];
19
+ approveAbove: number;
20
+ format: "infracost";
21
+ currency?: string;
22
+ }
23
+
24
+ /** A cost hold that prices each root with infracost, waiting over `approveAbove` a month. `command` replaces infracost's argv; it must name `{planJson}`. */
25
+ export function terraformCostHold(options: { approveAbove: number; currency?: string; command?: string[] }): TerraformCostHold {
26
+ const command = options.command ?? [...INFRACOST_ARGV];
27
+ if (!command.some((a) => a.includes("{planJson}"))) throw new Error("terraformCostHold: the command must name {planJson}, the root's plan document");
28
+ return { command, approveAbove: options.approveAbove, format: "infracost", ...(options.currency ? { currency: options.currency } : {}) };
29
+ }