@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
@@ -1,5 +1,10 @@
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
1
4
  import { describe, test, expect } from "vitest";
2
- import { blocksToEntities, type TerraformEntity } from "./parse";
5
+ import type { Declarable } from "@intentius/chant/declarable";
6
+ import { blocksToEntities, jsonConfigTree, parseTerraformRootContent, parseTerraformRootDir, type TerraformEntity } from "./parse";
7
+ import { terraformPlugin } from "../plugin";
3
8
 
4
9
  describe("blocksToEntities: suppression attachment (chant #2111)", () => {
5
10
  test("attaches the block's start line even with no suppression comment", async () => {
@@ -92,3 +97,136 @@ describe("Terragrunt config files (#3417)", () => {
92
97
  expect([...entities.values()].map((e) => e.entityType)).toEqual(["Terraform::Terraform"]);
93
98
  });
94
99
  });
100
+
101
+ // ── chant#4457: `.tf.json` and `.tofu` files ───────────────────────────────
102
+
103
+ describe("configuration files beyond `.tf` (chant#4457)", () => {
104
+ const configFiles = join(dirname(fileURLToPath(import.meta.url)), "..", "__fixtures__", "config-files");
105
+ const at = (name: string): string => join(configFiles, name);
106
+
107
+ /** What the acceptance compares: each entity's type, address, body and file. */
108
+ const shape = (entities: Map<string, Declarable>): Record<string, { type: string; address: string; body: unknown; file: string }> =>
109
+ Object.fromEntries(
110
+ [...entities].map(([key, e]) => {
111
+ const p = (e as TerraformEntity).props;
112
+ return [key, { type: e.entityType, address: p.address, body: p.body, file: p.file }];
113
+ }),
114
+ );
115
+ /** The same, with each file's suffix taken off so two syntaxes of one file compare equal. */
116
+ const sameFile = (entities: Map<string, Declarable>, suffix: string) =>
117
+ Object.fromEntries(Object.entries(shape(entities)).map(([k, v]) => [k, { ...v, file: v.file.replace(suffix, "") }]));
118
+
119
+ test("a block in main.tf.json is the entity the same block in main.tf is", async () => {
120
+ const tf = await parseTerraformRootDir(at("tf"), "app");
121
+ const json = await parseTerraformRootDir(at("tf-json"), "app");
122
+ expect(Object.keys(shape(json)).sort()).toEqual(["app/aws_s3_bucket.assets", "app/var.region"]);
123
+ expect(shape(json)["app/aws_s3_bucket.assets"]).toEqual({ ...shape(tf)["app/aws_s3_bucket.assets"], file: "main.tf.json" });
124
+ expect(sameFile(json, ".tf.json")).toEqual(sameFile(tf, ".tf"));
125
+ });
126
+
127
+ test("a main.tofu and a main.tofu.json root yields its entities under tofu and choudoufu", async () => {
128
+ const tf = sameFile(await parseTerraformRootDir(at("tf"), "app"), ".tf");
129
+ for (const binary of ["tofu", "choudoufu", "/usr/local/bin/tofu"]) {
130
+ expect(sameFile(await parseTerraformRootDir(at("tofu"), "app", undefined, { binary }), ".tofu")).toEqual(tf);
131
+ expect(sameFile(await parseTerraformRootDir(at("tofu-json"), "app", undefined, { binary }), ".tofu.json")).toEqual(tf);
132
+ }
133
+ });
134
+
135
+ test("a terraform root, or one with no binary set, reads no .tofu file", async () => {
136
+ expect((await parseTerraformRootDir(at("tofu"), "app", undefined, { binary: "terraform" })).size).toBe(0);
137
+ expect((await parseTerraformRootDir(at("tofu-json"), "app")).size).toBe(0);
138
+ });
139
+
140
+ test("x.tofu shadows x.tf in a tofu or choudoufu root, and a terraform root reads x.tf", async () => {
141
+ for (const binary of ["tofu", "choudoufu"]) {
142
+ const keys = [...(await parseTerraformRootDir(at("shadow"), "app", undefined, { binary })).keys()].sort();
143
+ expect(keys).toEqual(["app/aws_s3_bucket.from_tofu", "app/output.kept"]);
144
+ }
145
+ const keys = [...(await parseTerraformRootDir(at("shadow"), "app", undefined, { binary: "terraform" })).keys()].sort();
146
+ expect(keys).toEqual(["app/aws_s3_bucket.from_tf", "app/output.kept"]);
147
+ });
148
+
149
+ test("a root whose only declaration is in a .tofu file builds its entities through buildRoots()", async () => {
150
+ const hook = terraformPlugin.buildRoots!;
151
+ const config = (binary: string) => ({ terraform: { binary, roots: { only: { dir: "tofu-only" } } } });
152
+ const tofu = await hook({ projectRoot: configFiles, config: config("tofu"), entities: new Map() });
153
+ expect([...tofu.entities.keys()]).toEqual(["only/aws_s3_bucket.only"]);
154
+ expect(((tofu.entities.get("only/aws_s3_bucket.only") as TerraformEntity).props).file).toBe("main.tofu");
155
+ const terraform = await hook({ projectRoot: configFiles, config: config("terraform"), entities: new Map() });
156
+ expect(terraform.entities.size).toBe(0);
157
+ });
158
+
159
+ /** The joined form `chant audit` builds for a directory, every file behind a `# file:` line. */
160
+ const bundle = (dir: string): string =>
161
+ readdirSync(dir)
162
+ .sort()
163
+ .map((name) => `# file: ${name}\n${readFileSync(join(dir, name), "utf-8")}`)
164
+ .join("\n");
165
+
166
+ test("the audit path sees the entities the build path sees for each fixture", async () => {
167
+ for (const [name, binary] of [
168
+ ["tf", undefined],
169
+ ["tf-json", undefined],
170
+ ["tofu", "tofu"],
171
+ ["tofu-json", "tofu"],
172
+ ["tofu-only", "choudoufu"],
173
+ ["shadow", "tofu"],
174
+ ["shadow", "terraform"],
175
+ ] as const) {
176
+ const opts = binary ? { binary } : undefined;
177
+ const built = shape(await parseTerraformRootDir(at(name), "app", undefined, opts));
178
+ const audited = shape(await parseTerraformRootContent(bundle(at(name)), "app", undefined, opts));
179
+ expect(audited).toEqual(built);
180
+ }
181
+ });
182
+
183
+ test("the audit hook reads a root carrying a .tofu file as a tofu root", async () => {
184
+ const entities = await terraformPlugin.auditEntities!(bundle(at("shadow")));
185
+ expect([...entities.keys()].sort()).toEqual(["audit-root/aws_s3_bucket.from_tofu", "audit-root/output.kept"]);
186
+ });
187
+ });
188
+
189
+ describe("jsonConfigTree: Terraform's JSON syntax in hcl2json's shape (chant#4457)", () => {
190
+ test("wraps each block body in an array, one object level per label", () => {
191
+ const tree = jsonConfigTree(
192
+ JSON.stringify({
193
+ "//": "a comment Terraform ignores",
194
+ terraform: { required_version: ">= 1.5", backend: { s3: { bucket: "b", key: "k" } }, required_providers: { aws: { source: "hashicorp/aws" } } },
195
+ locals: { a: 1 },
196
+ provider: { aws: [{ region: "us-east-1" }, { alias: "west", region: "us-west-2" }] },
197
+ resource: { aws_instance: { web: { ami: "x", lifecycle: { create_before_destroy: true } } } },
198
+ data: { aws_ami: { ubuntu: { most_recent: true } } },
199
+ }),
200
+ );
201
+ expect(tree).toEqual({
202
+ terraform: [{ required_version: ">= 1.5", backend: { s3: [{ bucket: "b", key: "k" }] }, required_providers: [{ aws: { source: "hashicorp/aws" } }] }],
203
+ locals: [{ a: 1 }],
204
+ provider: { aws: [{ region: "us-east-1" }, { alias: "west", region: "us-west-2" }] },
205
+ resource: { aws_instance: { web: [{ ami: "x", lifecycle: [{ create_before_destroy: true }] }] } },
206
+ data: { aws_ami: { ubuntu: [{ most_recent: true }] } },
207
+ });
208
+ });
209
+
210
+ test("a live block in a .tf.json terraform block declares the estate", async () => {
211
+ const source = JSON.stringify({ terraform: { live: { estate: "prod" } } });
212
+ const entities = await blocksToEntities([{ name: "main.tf.json", source }], "app", undefined, { binary: "choudoufu" });
213
+ const live = entities.get("app/live") as TerraformEntity;
214
+ expect(live.props.mode).toBe("live");
215
+ expect(live.props.estate).toBe("prod");
216
+ });
217
+
218
+ test("a bare expression JSON writes in a string reads as hcl2json writes the expression", () => {
219
+ const tree = jsonConfigTree(
220
+ JSON.stringify({
221
+ variable: { region: { type: "string" } },
222
+ resource: { aws_instance: { web: { provider: "aws.west", depends_on: ["aws_vpc.main"], ami: "${var.ami}" } } },
223
+ }),
224
+ );
225
+ expect(tree.variable).toEqual({ region: [{ type: "${string}" }] });
226
+ expect(tree.resource).toEqual({ aws_instance: { web: [{ provider: "${aws.west}", depends_on: ["${aws_vpc.main}"], ami: "${var.ami}" }] } });
227
+ });
228
+
229
+ test("malformed JSON throws, as malformed HCL does", () => {
230
+ expect(() => jsonConfigTree("{ not json")).toThrow();
231
+ });
232
+ });
package/src/hcl/parse.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * The one shared HCL parse for the terraform lexicon.
3
3
  *
4
- * Two entry points, one entity shape. `parseTerraformRootDir` reads the `.tf`
5
- * files of a directory (non-recursive, matching Terraform's own module
6
- * scoping) and is what `buildRoots()` calls. `parseTerraformRootContent` takes
7
- * the joined-file string `chant audit` hands a lexicon for a discovered root
8
- * module: every `.tf` in filename order behind a `# file: <name>` line comment.
4
+ * Two entry points, one entity shape. `parseTerraformRootDir` reads the
5
+ * configuration files of a directory (non-recursive, matching Terraform's own
6
+ * module scoping; `.tf` and `.tf.json`, plus `.tofu` and `.tofu.json` for a
7
+ * tofu or choudoufu root, see `./files.ts`) and is what `buildRoots()` calls.
8
+ * `parseTerraformRootContent` takes the joined-file string `chant audit` hands
9
+ * a lexicon for a discovered root module: every configuration file in filename
10
+ * order behind a `# file: <name>` line comment, narrowed to the same set.
9
11
  * Both funnel through `blocksToEntities`, so an entity the audit path sees is
10
12
  * the entity the build path sees.
11
13
  *
@@ -18,7 +20,7 @@
18
20
  * changed in `packages/core/package.json` for this.
19
21
  */
20
22
 
21
- import { existsSync, readdirSync, readFileSync } from "node:fs";
23
+ import { existsSync, readFileSync } from "node:fs";
22
24
  import { join } from "node:path";
23
25
  import { loadHcl2json, type Hcl2Json } from "@intentius/chant/terraform/hcl2json";
24
26
  import { DECLARABLE_MARKER, type Declarable } from "@intentius/chant/declarable";
@@ -27,6 +29,20 @@ import type { SuppressionDirective } from "@intentius/chant/lint/suppressions";
27
29
  import type { TerraformDeleteMode } from "../config";
28
30
  import { redactSensitiveBody, redactSensitiveSource } from "./redact";
29
31
  import { scanSuppressions, directivesFor, type FileScan } from "./suppressions";
32
+ import { isJsonConfigFile, jsonConfigTree, listConfigFiles, selectConfigFiles } from "./files";
33
+
34
+ export {
35
+ TERRAFORM_SUFFIXES,
36
+ TOFU_SUFFIXES,
37
+ isConfigFileFor,
38
+ isJsonConfigFile,
39
+ isTofuConfigFile,
40
+ jsonConfigTree,
41
+ listConfigFiles,
42
+ listConfigFilesSafe,
43
+ readsTofuFiles,
44
+ selectConfigFiles,
45
+ } from "./files";
30
46
 
31
47
  /** A parsed HCL block body, as `@cdktn/hcl2json` encodes it. */
32
48
  export type BlockBody = Record<string, unknown>;
@@ -40,6 +56,10 @@ export type BlockBody = Record<string, unknown>;
40
56
  * the instance count is a plan-time answer and often a state-time one, but it
41
57
  * is a shape a reader should be told rather than left to infer from a node
42
58
  * count that never grows.
59
+ *
60
+ * Since #4454 a literal expansion also gets its instance nodes, children of
61
+ * the block's node (`TerraformEntity.instanceKeys`). The block node stays,
62
+ * and the edges stay block to block.
43
63
  */
44
64
  export type TerraformExpansion = "count" | "for_each";
45
65
 
@@ -151,6 +171,27 @@ export interface TerraformEntity extends Declarable {
151
171
  * ROOT's name, not the module's: the module is not separately applied.
152
172
  */
153
173
  readonly stack: string;
174
+ /**
175
+ * The instance keys a `resource` or `data` block's literal `count` or
176
+ * `for_each` expands to (#4454), as Terraform prints them: `[0]`, `[1]`
177
+ * for `count = 2`, `["a"]` for a `for_each` over a literal map or
178
+ * `toset([...])`. Core's `withInstanceNodes` reads this lexicon-neutral
179
+ * field, as `buildGraphIr` reads `stack`, and draws one instance node per
180
+ * key under the block's node. Absent when the block is not expanded or its
181
+ * expansion is not a literal. Since #4486 `./instances.ts` also sets it
182
+ * for an expansion it can count from the root's variables (`count = var.n`
183
+ * with `n` known), and for a `module` call.
184
+ */
185
+ readonly instanceKeys?: readonly string[];
186
+ /**
187
+ * The instance nodes of a block under an expanded module call (#4486), by
188
+ * full node id: `estate/module.app[0]/aws_eip.pool[1]`, with `key` the
189
+ * block's own key (`[1]`, absent when the block is not expanded itself)
190
+ * and `scope` the module path as Terraform prints it (`module.app[0]`).
191
+ * Core's `entityInstances` reads this in place of `instanceKeys`, which
192
+ * such a block never carries. Set by `./instances.ts`.
193
+ */
194
+ readonly instances?: ReadonlyArray<{ readonly id: string; readonly key?: string; readonly scope?: string }>;
154
195
  /**
155
196
  * Every reference in this block, resolved to the entity keys it points at
156
197
  * (chant #2265), in core's lexicon-neutral {@link EntityReference} shape.
@@ -183,6 +224,23 @@ export const OUTPUT_TYPE = "Terraform::Output";
183
224
  export const LOCALS_TYPE = "Terraform::Locals";
184
225
  /** A `live { estate = "..." }` block, or an `estate.chdf.hcl` sidecar's content (#2103). */
185
226
  export const LIVE_TYPE = "Terraform::Live";
227
+ /**
228
+ * The four configuration blocks that change how a plan treats addresses
229
+ * rather than declaring anything (#4458): `moved`, `import`, `removed` and
230
+ * `check`. The first three carry no label and key as `moved`, `import`,
231
+ * `removed` (numbered `~2`, `~3` when a root holds several, as `locals` is);
232
+ * a `check` keys as `check.<name>`. Their references are edges like any other
233
+ * block's: a `moved` block's `from` and `to`, an `import` block's `to`, a
234
+ * `removed` block's `from`, and everything a `check` block's assertions and
235
+ * scoped `data` blocks read. A `from` usually names an address that is no
236
+ * longer declared, which resolves to no entity and so draws no edge, and
237
+ * that is all that happens: an unresolved reference is dropped, never
238
+ * reported (`./edges.ts`).
239
+ */
240
+ export const MOVED_TYPE = "Terraform::Moved";
241
+ export const IMPORT_TYPE = "Terraform::Import";
242
+ export const REMOVED_TYPE = "Terraform::Removed";
243
+ export const CHECK_TYPE = "Terraform::Check";
186
244
 
187
245
  /**
188
246
  * Terragrunt config entities (#3417). A `terragrunt.hcl` or `root.hcl` is not
@@ -214,6 +272,55 @@ function expansionOf(body: BlockBody): TerraformExpansion | undefined {
214
272
  return undefined;
215
273
  }
216
274
 
275
+ /** The most instance keys read off one literal; past it the block draws as the block alone. */
276
+ const MAX_LITERAL_INSTANCES = 256;
277
+
278
+ /**
279
+ * The instance keys a literal expansion gives (#4454), as Terraform prints
280
+ * them, or undefined when the expansion is not a literal a read of the
281
+ * source can count. `count` as a number (`count = 3`), `for_each` as a
282
+ * literal map (`{ a = 1 }`, keys in Terraform's lexical order) or
283
+ * `toset([...])` over string literals. Anything holding a reference is a
284
+ * plan-time answer.
285
+ */
286
+ export function literalInstanceKeys(body: BlockBody): string[] | undefined {
287
+ const keys = literalKeys(body);
288
+ if (keys === undefined || keys.length > MAX_LITERAL_INSTANCES) return undefined;
289
+ return keys;
290
+ }
291
+
292
+ function literalKeys(body: BlockBody): string[] | undefined {
293
+ if ("count" in body) {
294
+ const count = body.count;
295
+ const n = typeof count === "number" ? count
296
+ : typeof count === "string" && /^\$\{\s*\d+\s*\}$/.test(count.trim()) ? Number(count.trim().slice(2, -1))
297
+ : undefined;
298
+ if (n === undefined || !Number.isInteger(n) || n < 0) return undefined;
299
+ return Array.from({ length: n }, (_, i) => `[${i}]`);
300
+ }
301
+ if ("for_each" in body) {
302
+ const each = body.for_each;
303
+ if (typeof each === "object" && each !== null && !Array.isArray(each)) {
304
+ const names = Object.keys(each);
305
+ if (names.some((k) => k.includes("${"))) return undefined;
306
+ return names.sort().map((k) => `[${JSON.stringify(k)}]`);
307
+ }
308
+ if (typeof each === "string") {
309
+ const m = /^\$\{\s*toset\(\s*(\[[\s\S]*\])\s*\)\s*\}$/.exec(each.trim());
310
+ if (!m) return undefined;
311
+ let list: unknown;
312
+ try {
313
+ list = JSON.parse(m[1]!.replace(/,\s*\]$/, "]"));
314
+ } catch {
315
+ return undefined;
316
+ }
317
+ if (!Array.isArray(list) || !list.every((v) => typeof v === "string")) return undefined;
318
+ return [...new Set(list as string[])].sort().map((k) => `[${JSON.stringify(k)}]`);
319
+ }
320
+ }
321
+ return undefined;
322
+ }
323
+
217
324
  /**
218
325
  * Build one entity. Written out rather than run through `createResource`
219
326
  * from `@intentius/chant/runtime`: that factory is for generated resource
@@ -234,6 +341,7 @@ export function terraformEntity(
234
341
  suppressions?: readonly SuppressionDirective[],
235
342
  ): TerraformEntity {
236
343
  const expansion = expansionOf(body);
344
+ const instanceKeys = expansion && (entityType === RESOURCE_TYPE || entityType === DATA_TYPE) ? literalInstanceKeys(body) : undefined;
237
345
  const labels = entityType === RESOURCE_TYPE ? blockLabels(address)
238
346
  : entityType === DATA_TYPE && address.startsWith("data.") ? blockLabels(address.slice("data.".length))
239
347
  : undefined;
@@ -243,6 +351,7 @@ export function terraformEntity(
243
351
  entityType,
244
352
  kind: "resource",
245
353
  stack: root,
354
+ ...(instanceKeys ? { instanceKeys } : {}),
246
355
  props: {
247
356
  address,
248
357
  ...(labels ?? {}),
@@ -302,11 +411,20 @@ export function splitBundleContent(content: string, fallbackName = "main.tf"): T
302
411
  return files.filter((f) => f.source.trim() !== "");
303
412
  }
304
413
 
305
- /** Every `.tf` directly under `dir`, in filename order. Non-recursive. */
306
- export function listTerraformFiles(dir: string): string[] {
307
- return readdirSync(dir)
308
- .filter((f) => f.endsWith(".tf"))
309
- .sort();
414
+ /**
415
+ * Every configuration file directly under `dir` that a root run by `binary`
416
+ * reads, in filename order, non-recursive: `.tf` and `.tf.json`, plus `.tofu`
417
+ * and `.tofu.json` for a tofu or choudoufu root, with `x.tofu` shadowing
418
+ * `x.tf` (chant#4457). The shared lister is `./files.ts`'s `listConfigFiles`.
419
+ */
420
+ export function listTerraformFiles(dir: string, binary?: string): string[] {
421
+ return listConfigFiles(dir, binary);
422
+ }
423
+
424
+ /** One file's tree in hcl2json's shape: hcl2json for HCL, {@link jsonConfigTree} for Terraform's JSON syntax. */
425
+ export async function parseFileTree(parser: Hcl2Json, file: TerraformFile): Promise<Record<string, unknown>> {
426
+ if (isJsonConfigFile(file.name)) return jsonConfigTree(file.source);
427
+ return (await parser.parse(file.name, file.source)) as Record<string, unknown>;
310
428
  }
311
429
 
312
430
  /** The Terragrunt config files directly under `dir` (`root.hcl`, `terragrunt.hcl`), in filename order like the audit's bundle. */
@@ -488,7 +606,7 @@ export async function blocksToEntities(
488
606
  };
489
607
 
490
608
  for (const file of files) {
491
- const tree = (await parser.parse(file.name, file.source)) as Record<string, unknown>;
609
+ const tree = await parseFileTree(parser, file);
492
610
  const scan = scanSuppressions(file.name, file.source);
493
611
  if (isTerragruntFileName(file.name)) {
494
612
  terragruntBlocks(tree, file, scan, add);
@@ -503,6 +621,10 @@ export async function blocksToEntities(
503
621
  oneLabel(tree, "output", OUTPUT_TYPE, (n) => `output.${n}`, file, scan);
504
622
  twoLabels(tree, "resource", RESOURCE_TYPE, (t, n) => `${t}.${n}`, file, scan);
505
623
  twoLabels(tree, "data", DATA_TYPE, (t, n) => `data.${t}.${n}`, file, scan);
624
+ unlabelled(tree, "moved", MOVED_TYPE, file, scan);
625
+ unlabelled(tree, "import", IMPORT_TYPE, file, scan);
626
+ unlabelled(tree, "removed", REMOVED_TYPE, file, scan);
627
+ oneLabel(tree, "check", CHECK_TYPE, (n) => `check.${n}`, file, scan);
506
628
  }
507
629
 
508
630
  if (sidecar) {
@@ -568,8 +690,9 @@ function terragruntBlocks(
568
690
  }
569
691
 
570
692
  /**
571
- * Parse a root module directory. Reads every `.tf` directly under `dir`, plus
572
- * the `estate.chdf.hcl` sidecar beside them when present. Throws whatever the
693
+ * Parse a root module directory. Reads the configuration files directly under
694
+ * `dir` that `modeOptions.binary` reads ({@link listTerraformFiles}), plus the
695
+ * `estate.chdf.hcl` sidecar beside them when present. Throws whatever the
573
696
  * parser throws for malformed HCL; `buildRoots()` is where that becomes a
574
697
  * warning.
575
698
  */
@@ -581,7 +704,7 @@ export async function parseTerraformRootDir(
581
704
  callers: readonly string[] = [],
582
705
  ): Promise<Map<string, Declarable>> {
583
706
  // A child module is a Terraform module, never a Terragrunt unit (#3417).
584
- const names = [...listTerraformFiles(dir), ...(callers.length === 0 ? listTerragruntFiles(dir) : [])];
707
+ const names = [...listTerraformFiles(dir, modeOptions?.binary), ...(callers.length === 0 ? listTerragruntFiles(dir) : [])];
585
708
  const files = names.map((name) => ({
586
709
  name,
587
710
  source: readFileSync(join(dir, name), "utf-8"),
@@ -610,7 +733,17 @@ export async function parseTerraformRootContent(
610
733
  hcl2json?: Hcl2Json,
611
734
  modeOptions?: TerraformRootModeOptions,
612
735
  ): Promise<Map<string, Declarable>> {
613
- return blocksToEntities(splitBundleContent(content), root, hcl2json, modeOptions);
736
+ return blocksToEntities(bundleFilesFor(splitBundleContent(content), modeOptions?.binary), root, hcl2json, modeOptions);
737
+ }
738
+
739
+ /**
740
+ * The files of a joined bundle a root run by `binary` reads: the same set
741
+ * {@link listTerraformFiles} gives for a directory holding them, plus the
742
+ * Terragrunt files, in bundle order (chant#4457).
743
+ */
744
+ export function bundleFilesFor(files: readonly TerraformFile[], binary?: string): TerraformFile[] {
745
+ const keep = new Set(selectConfigFiles(files.map((f) => f.name), binary));
746
+ return files.filter((f) => keep.has(f.name) || isTerragruntFileName(f.name));
614
747
  }
615
748
 
616
749
  /** The root name an audit of the audited directory itself parses under. */
package/src/hcl/roots.ts CHANGED
@@ -34,6 +34,7 @@ import type { TerraformRootConfig } from "../config";
34
34
  import { LIVE_TYPE, parseTerraformRootDir } from "./parse";
35
35
  import { descendModules, resolveCallModuleType, type CallModuleType } from "./descend";
36
36
  import { resolveEntityReferences } from "./edges";
37
+ import { resolveInstanceNodes } from "./instances";
37
38
 
38
39
  export interface TerraformRootsResult {
39
40
  entities: Map<string, Declarable>;
@@ -111,6 +112,11 @@ export async function renderTerraformRoots(
111
112
  for (const [key, entity] of descended.entities) entities.set(key, entity);
112
113
  warnings.push(...descended.warnings);
113
114
 
115
+ // Instance nodes the root's variables let the declared graph count,
116
+ // and those under expanded module calls (#4486). Needs the root and
117
+ // every child scope parsed, so it runs once the descent has.
118
+ await resolveInstanceNodes(entities, name, { dir, ...(root.varFiles ? { varFiles: root.varFiles } : {}), hcl2json: opts.hcl2json });
119
+
114
120
  // A `live` block or `estate.chdf.hcl` sidecar only takes effect under
115
121
  // choudoufu; under any other binary it parses fine but is inert, which
116
122
  // is worth a warning rather than silence (#2103).
@@ -27,9 +27,9 @@
27
27
  import type { SuppressionDirective, SuppressionIds } from "@intentius/chant/lint/suppressions";
28
28
 
29
29
  /** A block header keyword with zero labels (`terraform {`, `locals {`). */
30
- const BARE_RE = /^\s*(terraform|locals|remote_state)\s*\{/;
30
+ const BARE_RE = /^\s*(terraform|locals|remote_state|moved|import|removed)\s*\{/;
31
31
  /** One label (`provider "aws" {`, `module "vpc" {`, `variable "x" {`, `output "x" {`). */
32
- const ONE_LABEL_RE = /^\s*(provider|module|variable|output|dependency|generate)\s+"([^"]*)"\s*\{/;
32
+ const ONE_LABEL_RE = /^\s*(provider|module|variable|output|check|dependency|generate)\s+"([^"]*)"\s*\{/;
33
33
  /** Two labels (`resource "aws_s3_bucket" "assets" {`, `data "aws_ami" "x" {`). */
34
34
  const TWO_LABEL_RE = /^\s*(resource|data)\s+"([^"]*)"\s+"([^"]*)"\s*\{/;
35
35
 
@@ -45,6 +45,12 @@ function addressFor(keyword: string, labels: string[]): string {
45
45
  return "locals";
46
46
  case "remote_state":
47
47
  return "remote_state";
48
+ case "moved":
49
+ case "import":
50
+ case "removed":
51
+ return keyword;
52
+ case "check":
53
+ return `check.${labels[0]}`;
48
54
  case "dependency":
49
55
  return `dependency.${labels[0]}`;
50
56
  case "generate":
package/src/index.ts CHANGED
@@ -10,10 +10,86 @@ export {
10
10
  terraformApplyCapability,
11
11
  TerraformPlanMovedError,
12
12
  TERRAFORM_VERB_FAMILIES,
13
+ // #4455: a choudoufu member's outputs, read for core's `member-outputs`.
14
+ choudoufuOutputSource,
15
+ choudoufuMemberPlannedOutputs,
13
16
  type TerraformApplyStepInput,
14
17
  type TerraformApplyStepOutput,
15
18
  } from "./components/capability-plugin";
16
19
 
20
+ // Composed runs (#4442): the working state merged and split, the composed
21
+ // plan cut into one plan per member, and each member's apply outcome.
22
+ export { mergeStates, splitState, splitStates, derivedLineage, COMPOSE_LINEAGE, type SplitState } from "./compose/state-merge";
23
+ export {
24
+ slicePlan as sliceComposedPlan,
25
+ sliceText,
26
+ planCounts,
27
+ composedReads,
28
+ composedMemberPlans,
29
+ type PlanCounts,
30
+ type ComposedMemberRead,
31
+ type ComposedMemberPlan,
32
+ type ComposedMemberPlansOptions,
33
+ } from "./compose/slice";
34
+ export { readApplyEvents, memberApply, composedApplyOutcomes, type ApplyEvents, type MemberApply, type MemberApplyOutcome } from "./compose/apply-events";
35
+ export { topModule, stripModule, stripChange, moduleChanges } from "./compose/module-address";
36
+
37
+ // Composed runs of choudoufu estates (#4445): one plan and one apply, values read from a post-apply plan.
38
+ export {
39
+ EstateRun,
40
+ estateVersionFallback,
41
+ estateOutcomes,
42
+ readEstateApplyText,
43
+ type ComposedRunner,
44
+ type ComposedRunPlan,
45
+ type ComposedRunApply,
46
+ type ComposedMemberOutcome,
47
+ type EstateRunOptions,
48
+ } from "./compose/estate";
49
+ export { priorStateValues, stateResources, stateOutputs, type StateResourceValues } from "./applied-state";
50
+
51
+ // Composed runs over roots with state (#4444): one plan and one apply, each
52
+ // state written back whatever happened. Implements `ComposedRunner` (#4445).
53
+ export {
54
+ ComposedRun,
55
+ composedPlanDigest,
56
+ changesSomething,
57
+ type ComposedRunOptions,
58
+ type ComposedRunStage,
59
+ type StateRunPlan,
60
+ type StateRunApply,
61
+ type StateMemberOutcome,
62
+ type ComposedStateWrite,
63
+ type ComposeHook,
64
+ type ComposeHookMoment,
65
+ } from "./compose/run";
66
+
67
+ // The plan shape of a `show -json` plan (#4436), for core's `coveredRound`.
68
+ export { terraformPlanShape, type TerraformPlanShapeOptions } from "./plan-shape";
69
+ export { terraformLockRows, terraformLockRowsFromPart, terraformDefaultLockGrain, terraformLockGrainRefusal } from "./lock-rows";
70
+
71
+ // Composed runs (#4419, #4438): which roots compose, in what order, and why
72
+ // the others fall back. Off unless `terraform.compose.enabled` is set.
73
+ export {
74
+ compositionFor,
75
+ compositionIfEnabled,
76
+ composeOff,
77
+ composeSet,
78
+ readOrder,
79
+ moduleNames,
80
+ fallbackLine,
81
+ withPlanFailed,
82
+ identityKey,
83
+ DEFAULT_COMPOSE_MAX,
84
+ COMPOSE_STEP_KIND,
85
+ type Composition,
86
+ type CompositionInput,
87
+ type ComposedRead,
88
+ type ComposeFallback,
89
+ type ComposeFallbackKind,
90
+ type ExpectedBinary,
91
+ } from "./compose";
92
+
17
93
  // Serializer
18
94
  export { terraformSerializer } from "./serializer";
19
95
 
@@ -68,6 +144,7 @@ export {
68
144
  choudoufuLiveLs,
69
145
  choudoufuLiveCheck,
70
146
  choudoufuAdopt,
147
+ choudoufuPlanSet,
71
148
  terraformPinRollout,
72
149
  } from "./op/builders";
73
150
 
@@ -132,3 +209,5 @@ export {
132
209
  type TerraformLiveRead,
133
210
  type TerraformPredictOptions,
134
211
  } from "./behaviour";
212
+ // terraform, tofu and choudoufu exit statuses on chant's exit-code table (#4553).
213
+ export { terraformExitOf, terraformPlanMoved, type TerraformExitSource } from "./exit-codes";
@@ -88,10 +88,11 @@ export const terraformAuditCatalog: Record<string, RuleMeta> = {
88
88
  tier: "merge-worthy",
89
89
  fixKind: "guidance",
90
90
  category: "correctness",
91
- title: "Live root declares a backend or cloud block, which choudoufu refuses",
91
+ title: "Live root declares a backend or cloud block, which choudoufu ignores",
92
92
  remediation:
93
- "Remove the `backend`/`cloud` block from the live root's terraform block; a live root's prior " +
94
- "state is a projection rebuilt from the live system every run, so there is no state to store.",
93
+ "Keep a `backend` block if other roots read this estate's outputs or the record store should be " +
94
+ "derived from its bucket; declare `record_store` to put records elsewhere. Remove a `cloud` block, " +
95
+ "which choudoufu drops entirely, or swap it for a `backend` block.",
95
96
  yamlBased: false,
96
97
  },
97
98
  TF025: {
@@ -551,6 +552,31 @@ export const terraformAuditCatalog: Record<string, RuleMeta> = {
551
552
  'Set `terragrunt_version_constraint = ">= 1.1.0"` in the root config. Off by default; judged only across the configs scanned.',
552
553
  yamlBased: false,
553
554
  },
555
+ TF046: {
556
+ id: "TF046",
557
+ tier: "report-only",
558
+ fixKind: "guidance",
559
+ category: "correctness",
560
+ title: "Live root resolves to the local record store in a CI run",
561
+ remediation:
562
+ "Keep or add a `backend` block of type s3, gcs or azurerm with a literal bucket, so choudoufu " +
563
+ "derives the record store from it, or declare `record_store` in the live block or `estate.chdf.hcl`. " +
564
+ "The rule is off by default and terragucci turns it on for its users; an emulator or test lane " +
565
+ "that keeps the local store on purpose sets `CHANT_ALLOW_LOCAL_RECORD_STORE=1`.",
566
+ yamlBased: false,
567
+ },
568
+ TF047: {
569
+ id: "TF047",
570
+ tier: "report-only",
571
+ fixKind: "guidance",
572
+ category: "correctness",
573
+ title: "Live root pins a module version the known-module catalog has no row for, or one with refusals",
574
+ remediation:
575
+ "Run `choudoufu live-check` on the root to see each refused site, or pin a version whose examples are clear in " +
576
+ "the static tier of choudoufu's module table (https://intentius.io/choudoufu/aws/compatibility/#modules). " +
577
+ "Off by default; terragucci turns it on for its users.",
578
+ yamlBased: false,
579
+ },
554
580
  };
555
581
 
556
582
  // Prior art credits, if any, live beside the rules in ./audit-lineage.ts (see
@@ -76,8 +76,8 @@
76
76
  *
77
77
  * TF024 (#2103, live root declares a backend/cloud block) credits choudoufu's
78
78
  * own `RuleStateBackend`, `internal/live/lint/issue.go` of
79
- * github.com/INTENTIUS/choudoufu: the identical condition, checked the same
80
- * way this fork checks it at `init` before any command runs. TF025 (a live
79
+ * github.com/INTENTIUS/choudoufu: the same condition, which choudoufu has
80
+ * reported as a warning since its #2213 (the block is ignored, not refused). TF025 (a live
81
81
  * root's non-default `terraform.workspace`) has no entry: its workspace half
82
82
  * is refused at the CLI argument-parsing layer there
83
83
  * (`internal/command/live_mode.go`), not by a dedicated lint rule under
@@ -110,6 +110,18 @@
110
110
  * a root's mode, and no surveyed tool has an Op model to read: choudoufu lints
111
111
  * HCL, and every tflint ruleset, tfsec/trivy-checks and the Sentinel policies
112
112
  * do the same. There is nothing upstream to credit.
113
+ *
114
+ * TF046 (chant#4561, a live root on the local record store in a CI run) has
115
+ * no entry. choudoufu prints where records go on every run and reports a
116
+ * `record_store.note` in `live-check -json` (INTENTIUS/choudoufu#2293), but
117
+ * it has no `internal/live/lint` rule for the local store, and no surveyed
118
+ * tool knows about record stores. TF044 is the nearest neighbour, for a
119
+ * Terragrunt state backend.
120
+ *
121
+ * TF047 (chant#4560, a live root pins a module version the known-module
122
+ * catalog has no row for, or one with refusals) has no entry. Its data is
123
+ * choudoufu's own module table (choudoufu#2292), vendored; no surveyed tool
124
+ * checks a module pin against a measured compatibility table.
113
125
  */
114
126
  import type { Lineage } from "@intentius/chant/audit/catalog";
115
127
 
@@ -0,0 +1,18 @@
1
+ terraform {
2
+ required_version = ">= 1.5.0"
3
+
4
+ live {
5
+ estate = "fixture-estate"
6
+
7
+ record_store "s3" {
8
+ bucket = "fixture-records"
9
+ }
10
+ }
11
+
12
+ backend "s3" {
13
+ bucket = "tfstate"
14
+ key = "fixture/terraform.tfstate"
15
+ }
16
+ }
17
+
18
+ resource "null_resource" "first" {}
@@ -0,0 +1 @@
1
+ estate = "fixture-estate"