@intentius/chant-lexicon-terraform 0.101.0 → 0.104.0

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 (374) hide show
  1. package/dist/__fixtures__/terragrunt/affected-cases.d.ts +23 -0
  2. package/dist/__fixtures__/terragrunt/affected-cases.d.ts.map +1 -0
  3. package/dist/__fixtures__/terragrunt/record.d.ts +17 -0
  4. package/dist/__fixtures__/terragrunt/record.d.ts.map +1 -0
  5. package/dist/change-set.d.ts +67 -0
  6. package/dist/change-set.d.ts.map +1 -0
  7. package/dist/changed-roots.d.ts +30 -0
  8. package/dist/changed-roots.d.ts.map +1 -0
  9. package/dist/codegen/package.d.ts +1 -1
  10. package/dist/commands.d.ts +26 -0
  11. package/dist/commands.d.ts.map +1 -0
  12. package/dist/components/capability-plugin.d.ts +54 -0
  13. package/dist/components/capability-plugin.d.ts.map +1 -0
  14. package/dist/composites/terraform-pin-rollout-op.d.ts +44 -0
  15. package/dist/composites/terraform-pin-rollout-op.d.ts.map +1 -0
  16. package/dist/config.d.ts +3 -3
  17. package/dist/hcl/descend.d.ts +1 -1
  18. package/dist/hcl/descend.d.ts.map +1 -1
  19. package/dist/hcl/edges.d.ts +2 -2
  20. package/dist/hcl/edges.d.ts.map +1 -1
  21. package/dist/hcl/jsonencode.d.ts +106 -0
  22. package/dist/hcl/jsonencode.d.ts.map +1 -0
  23. package/dist/hcl/marker.d.ts.map +1 -1
  24. package/dist/hcl/parse.d.ts +29 -1
  25. package/dist/hcl/parse.d.ts.map +1 -1
  26. package/dist/hcl/roots.d.ts +1 -1
  27. package/dist/hcl/roots.d.ts.map +1 -1
  28. package/dist/hcl/suppressions.d.ts.map +1 -1
  29. package/dist/index.d.ts +4 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/integrity.json +22 -4
  32. package/dist/lint/audit-catalog.d.ts.map +1 -1
  33. package/dist/lint/audit-lineage.d.ts +1 -1
  34. package/dist/lint/audit-lineage.d.ts.map +1 -1
  35. package/dist/lint/post-synth/flat-attributes.d.ts +69 -0
  36. package/dist/lint/post-synth/flat-attributes.d.ts.map +1 -0
  37. package/dist/lint/post-synth/index.d.ts.map +1 -1
  38. package/dist/lint/post-synth/module-source.d.ts +27 -1
  39. package/dist/lint/post-synth/module-source.d.ts.map +1 -1
  40. package/dist/lint/post-synth/terragrunt.d.ts +37 -0
  41. package/dist/lint/post-synth/terragrunt.d.ts.map +1 -0
  42. package/dist/lint/post-synth/tf002.d.ts +3 -1
  43. package/dist/lint/post-synth/tf002.d.ts.map +1 -1
  44. package/dist/lint/post-synth/tf030.d.ts +51 -0
  45. package/dist/lint/post-synth/tf030.d.ts.map +1 -0
  46. package/dist/lint/post-synth/tf031.d.ts +55 -0
  47. package/dist/lint/post-synth/tf031.d.ts.map +1 -0
  48. package/dist/lint/post-synth/tf032.d.ts +71 -0
  49. package/dist/lint/post-synth/tf032.d.ts.map +1 -0
  50. package/dist/lint/post-synth/tf033.d.ts +50 -0
  51. package/dist/lint/post-synth/tf033.d.ts.map +1 -0
  52. package/dist/lint/post-synth/tf034.d.ts +25 -0
  53. package/dist/lint/post-synth/tf034.d.ts.map +1 -0
  54. package/dist/lint/post-synth/tf035.d.ts +33 -0
  55. package/dist/lint/post-synth/tf035.d.ts.map +1 -0
  56. package/dist/lint/post-synth/tf036.d.ts +33 -0
  57. package/dist/lint/post-synth/tf036.d.ts.map +1 -0
  58. package/dist/lint/post-synth/tf037.d.ts +31 -0
  59. package/dist/lint/post-synth/tf037.d.ts.map +1 -0
  60. package/dist/lint/post-synth/tf038.d.ts +22 -0
  61. package/dist/lint/post-synth/tf038.d.ts.map +1 -0
  62. package/dist/lint/post-synth/tf039.d.ts +23 -0
  63. package/dist/lint/post-synth/tf039.d.ts.map +1 -0
  64. package/dist/lint/post-synth/tf040.d.ts +30 -0
  65. package/dist/lint/post-synth/tf040.d.ts.map +1 -0
  66. package/dist/lint/post-synth/tf041.d.ts +20 -0
  67. package/dist/lint/post-synth/tf041.d.ts.map +1 -0
  68. package/dist/lint/post-synth/tf042.d.ts +18 -0
  69. package/dist/lint/post-synth/tf042.d.ts.map +1 -0
  70. package/dist/lint/post-synth/tf043.d.ts +17 -0
  71. package/dist/lint/post-synth/tf043.d.ts.map +1 -0
  72. package/dist/lint/post-synth/tf044.d.ts +17 -0
  73. package/dist/lint/post-synth/tf044.d.ts.map +1 -0
  74. package/dist/lint/post-synth/tf045.d.ts +22 -0
  75. package/dist/lint/post-synth/tf045.d.ts.map +1 -0
  76. package/dist/manifest.json +1 -1
  77. package/dist/okf/index.md +16 -0
  78. package/dist/okf/rules/TF030.md +11 -0
  79. package/dist/okf/rules/TF031.md +11 -0
  80. package/dist/okf/rules/TF032.md +11 -0
  81. package/dist/okf/rules/TF033.md +11 -0
  82. package/dist/okf/rules/TF034.md +11 -0
  83. package/dist/okf/rules/TF035.md +11 -0
  84. package/dist/okf/rules/TF036.md +11 -0
  85. package/dist/okf/rules/TF037.md +11 -0
  86. package/dist/okf/rules/TF038.md +11 -0
  87. package/dist/okf/rules/TF039.md +11 -0
  88. package/dist/okf/rules/TF040.md +11 -0
  89. package/dist/okf/rules/TF041.md +11 -0
  90. package/dist/okf/rules/TF042.md +11 -0
  91. package/dist/okf/rules/TF043.md +11 -0
  92. package/dist/okf/rules/TF044.md +11 -0
  93. package/dist/okf/rules/TF045.md +11 -0
  94. package/dist/op/activities/index.d.ts +4 -0
  95. package/dist/op/activities/index.d.ts.map +1 -1
  96. package/dist/op/activities/pin-rollout.d.ts +46 -0
  97. package/dist/op/activities/pin-rollout.d.ts.map +1 -0
  98. package/dist/op/activities/terraform.d.ts +24 -50
  99. package/dist/op/activities/terraform.d.ts.map +1 -1
  100. package/dist/op/activity-contracts.d.ts +66 -18
  101. package/dist/op/activity-contracts.d.ts.map +1 -1
  102. package/dist/op/builders.d.ts +9 -0
  103. package/dist/op/builders.d.ts.map +1 -1
  104. package/dist/pin/edit-ts.d.ts +21 -0
  105. package/dist/pin/edit-ts.d.ts.map +1 -0
  106. package/dist/pin/edit.d.ts +52 -0
  107. package/dist/pin/edit.d.ts.map +1 -0
  108. package/dist/pin/forge.d.ts +39 -0
  109. package/dist/pin/forge.d.ts.map +1 -0
  110. package/dist/pin/index.d.ts +22 -0
  111. package/dist/pin/index.d.ts.map +1 -0
  112. package/dist/pin/rollout.d.ts +116 -0
  113. package/dist/pin/rollout.d.ts.map +1 -0
  114. package/dist/pin/scan.d.ts +54 -0
  115. package/dist/pin/scan.d.ts.map +1 -0
  116. package/dist/pin/source.d.ts +93 -0
  117. package/dist/pin/source.d.ts.map +1 -0
  118. package/dist/pin/waves.d.ts +66 -0
  119. package/dist/pin/waves.d.ts.map +1 -0
  120. package/dist/plan-digest.d.ts +59 -0
  121. package/dist/plan-digest.d.ts.map +1 -0
  122. package/dist/plugin.d.ts.map +1 -1
  123. package/dist/rules/flat-attributes.ts +146 -0
  124. package/dist/rules/module-source.ts +56 -1
  125. package/dist/rules/terragrunt.ts +76 -0
  126. package/dist/rules/tf002.ts +1 -1
  127. package/dist/rules/tf030.ts +290 -0
  128. package/dist/rules/tf031.ts +370 -0
  129. package/dist/rules/tf032.ts +226 -0
  130. package/dist/rules/tf033.ts +160 -0
  131. package/dist/rules/tf034.ts +63 -0
  132. package/dist/rules/tf035.ts +74 -0
  133. package/dist/rules/tf036.ts +119 -0
  134. package/dist/rules/tf037.ts +101 -0
  135. package/dist/rules/tf038.ts +71 -0
  136. package/dist/rules/tf039.ts +64 -0
  137. package/dist/rules/tf040.ts +76 -0
  138. package/dist/rules/tf041.ts +56 -0
  139. package/dist/rules/tf042.ts +42 -0
  140. package/dist/rules/tf043.ts +49 -0
  141. package/dist/rules/tf044.ts +51 -0
  142. package/dist/rules/tf045.ts +47 -0
  143. package/dist/terragrunt/affected.d.ts +119 -0
  144. package/dist/terragrunt/affected.d.ts.map +1 -0
  145. package/dist/terragrunt/index.d.ts +11 -0
  146. package/dist/terragrunt/index.d.ts.map +1 -0
  147. package/dist/terragrunt/mocks.d.ts +147 -0
  148. package/dist/terragrunt/mocks.d.ts.map +1 -0
  149. package/dist/terragrunt/run.d.ts +130 -0
  150. package/dist/terragrunt/run.d.ts.map +1 -0
  151. package/dist/terragrunt/units.d.ts +103 -0
  152. package/dist/terragrunt/units.d.ts.map +1 -0
  153. package/dist/terragrunt/wave.d.ts +89 -0
  154. package/dist/terragrunt/wave.d.ts.map +1 -0
  155. package/package.json +13 -3
  156. package/src/__fixtures__/change-set/choudoufu-set-plan.change-set.golden.json +95 -0
  157. package/src/__fixtures__/change-set/choudoufu-set-plan.json +76 -0
  158. package/src/__fixtures__/change-set/choudoufu.part.golden.json +173 -0
  159. package/src/__fixtures__/change-set/choudoufu.plan.json +610 -0
  160. package/src/__fixtures__/change-set/terraform.import-forget.plan.json +81 -0
  161. package/src/__fixtures__/change-set/terraform.part.golden.json +173 -0
  162. package/src/__fixtures__/change-set/terraform.plan.json +589 -0
  163. package/src/__fixtures__/change-set/tofu.part.golden.json +173 -0
  164. package/src/__fixtures__/change-set/tofu.plan.json +588 -0
  165. package/src/__fixtures__/change-set/v1.tf.txt +21 -0
  166. package/src/__fixtures__/change-set/v2-module-app.tf.txt +4 -0
  167. package/src/__fixtures__/change-set/v2.tf.txt +21 -0
  168. package/src/__fixtures__/pin/choudoufu-live-waves-n5.json +83 -0
  169. package/src/__fixtures__/pin/largeset-oci-n5/apply-order.txt +5 -0
  170. package/src/__fixtures__/pin/largeset-oci-n5/estates/e01/estate.chdf.hcl +6 -0
  171. package/src/__fixtures__/pin/largeset-oci-n5/estates/e01/main.tf +33 -0
  172. package/src/__fixtures__/pin/largeset-oci-n5/estates/e02/estate.chdf.hcl +6 -0
  173. package/src/__fixtures__/pin/largeset-oci-n5/estates/e02/main.tf +45 -0
  174. package/src/__fixtures__/pin/largeset-oci-n5/estates/e03/estate.chdf.hcl +6 -0
  175. package/src/__fixtures__/pin/largeset-oci-n5/estates/e03/main.tf +47 -0
  176. package/src/__fixtures__/pin/largeset-oci-n5/estates/e04/estate.chdf.hcl +6 -0
  177. package/src/__fixtures__/pin/largeset-oci-n5/estates/e04/main.tf +31 -0
  178. package/src/__fixtures__/pin/largeset-oci-n5/estates/e05/estate.chdf.hcl +6 -0
  179. package/src/__fixtures__/pin/largeset-oci-n5/estates/e05/main.tf +27 -0
  180. package/src/__fixtures__/pin/largeset-oci-n5/fixture.json +77 -0
  181. package/src/__fixtures__/plan-summary/largeset-n5-bump.json +2071 -0
  182. package/src/__fixtures__/plan-summary/largeset-n5.summary.golden.json +95 -0
  183. package/src/__fixtures__/plan-summary/largeset-n5.summary.golden.md +22 -0
  184. package/src/__fixtures__/plan-summary/largeset-n5.summary.golden.txt +12 -0
  185. package/src/__fixtures__/terragrunt/affected/catalog/units/web/terragrunt.hcl +3 -0
  186. package/src/__fixtures__/terragrunt/affected/gitignore +3 -0
  187. package/src/__fixtures__/terragrunt/affected/live/dev/app/terragrunt.hcl +3 -0
  188. package/src/__fixtures__/terragrunt/affected/live/dev/vpc/terragrunt.hcl +3 -0
  189. package/src/__fixtures__/terragrunt/affected/live/prod/app/terragrunt.hcl +3 -0
  190. package/src/__fixtures__/terragrunt/affected/live/stg/terragrunt.stack.hcl +5 -0
  191. package/src/__fixtures__/terragrunt/affected/modules/app/main.tf +6 -0
  192. package/src/__fixtures__/terragrunt/affected/modules/app/policy.json +1 -0
  193. package/src/__fixtures__/terragrunt/affected/modules/net/main.tf +2 -0
  194. package/src/__fixtures__/terragrunt/affected/modules/thing/main.tf +3 -0
  195. package/src/__fixtures__/terragrunt/affected/root.hcl +5 -0
  196. package/src/__fixtures__/terragrunt/affected-cases.ts +126 -0
  197. package/src/__fixtures__/terragrunt/five-units/catalog/units/thing/terragrunt.hcl +3 -0
  198. package/src/__fixtures__/terragrunt/five-units/live/dev/app/terragrunt.hcl +13 -0
  199. package/src/__fixtures__/terragrunt/five-units/live/dev/db/terragrunt.hcl +8 -0
  200. package/src/__fixtures__/terragrunt/five-units/live/dev/vpc/terragrunt.hcl +3 -0
  201. package/src/__fixtures__/terragrunt/five-units/live/prod/app/terragrunt.hcl +8 -0
  202. package/src/__fixtures__/terragrunt/five-units/live/prod/vpc/terragrunt.hcl +3 -0
  203. package/src/__fixtures__/terragrunt/five-units/modules/thing/main.tf +7 -0
  204. package/src/__fixtures__/terragrunt/five-units/root.hcl +5 -0
  205. package/src/__fixtures__/terragrunt/record.ts +56 -0
  206. package/src/__fixtures__/terragrunt/recorded/affected-find.json +50 -0
  207. package/src/__fixtures__/terragrunt/recorded/failed-plan-report.json +36 -0
  208. package/src/__fixtures__/terragrunt/recorded/find.json +67 -0
  209. package/src/__fixtures__/terragrunt/recorded/version.txt +1 -0
  210. package/src/__fixtures__/terragrunt/recorded/wave-1/apply-report.json +22 -0
  211. package/src/__fixtures__/terragrunt/recorded/wave-1/json/live/dev/vpc/tfplan.json +1 -0
  212. package/src/__fixtures__/terragrunt/recorded/wave-1/json/live/prod/vpc/tfplan.json +1 -0
  213. package/src/__fixtures__/terragrunt/recorded/wave-1/plan-report.json +22 -0
  214. package/src/__fixtures__/terragrunt/recorded/wave-2/json/live/dev/db/tfplan.json +1 -0
  215. package/src/__fixtures__/terragrunt/recorded/wave-2/json/live/prod/app/tfplan.json +1 -0
  216. package/src/__fixtures__/terragrunt/recorded/wave-2/plan-report.json +22 -0
  217. package/src/change-set-combined-run.test.ts +156 -0
  218. package/src/change-set.test.ts +151 -0
  219. package/src/change-set.ts +273 -0
  220. package/src/changed-roots.test.ts +58 -0
  221. package/src/changed-roots.ts +102 -0
  222. package/src/commands.ts +112 -0
  223. package/src/components/capability-plugin.test.ts +91 -0
  224. package/src/components/capability-plugin.ts +131 -0
  225. package/src/components/pr-loop.tofu.test.ts +244 -0
  226. package/src/composites/terraform-pin-rollout-op.test.ts +69 -0
  227. package/src/composites/terraform-pin-rollout-op.ts +60 -0
  228. package/src/hcl/descend.ts +1 -1
  229. package/src/hcl/edges.ts +2 -2
  230. package/src/hcl/jsonencode.test.ts +226 -0
  231. package/src/hcl/jsonencode.ts +454 -0
  232. package/src/hcl/parse.test.ts +25 -0
  233. package/src/hcl/parse.ts +91 -6
  234. package/src/hcl/roots.ts +1 -1
  235. package/src/hcl/suppressions.ts +8 -2
  236. package/src/index.ts +18 -0
  237. package/src/lint/audit-catalog.ts +230 -0
  238. package/src/lint/audit-lineage.ts +155 -1
  239. package/src/lint/audit.test.ts +3 -1
  240. package/src/lint/post-synth/estate-2284.test.ts +68 -0
  241. package/src/lint/post-synth/fixtures/TF030/negative-rules.tf +45 -0
  242. package/src/lint/post-synth/fixtures/TF030/negative.tf +55 -0
  243. package/src/lint/post-synth/fixtures/TF030/not-determined-port.tf +28 -0
  244. package/src/lint/post-synth/fixtures/TF030/not-determined-standalone.tf +32 -0
  245. package/src/lint/post-synth/fixtures/TF030/not-determined-vpc-rule.tf +21 -0
  246. package/src/lint/post-synth/fixtures/TF030/not-determined.tf +39 -0
  247. package/src/lint/post-synth/fixtures/TF030/positive-ipv6.tf +44 -0
  248. package/src/lint/post-synth/fixtures/TF030/positive-mixed.tf +36 -0
  249. package/src/lint/post-synth/fixtures/TF030/positive-standalone.tf +25 -0
  250. package/src/lint/post-synth/fixtures/TF030/positive.tf +43 -0
  251. package/src/lint/post-synth/fixtures/TF031/negative.tf +89 -0
  252. package/src/lint/post-synth/fixtures/TF031/not-determined.tf +139 -0
  253. package/src/lint/post-synth/fixtures/TF031/positive-data-source.tf +57 -0
  254. package/src/lint/post-synth/fixtures/TF031/positive-heredoc.tf +149 -0
  255. package/src/lint/post-synth/fixtures/TF031/positive-jsonencode.tf +115 -0
  256. package/src/lint/post-synth/fixtures/TF032/negative-heredoc.tf +20 -0
  257. package/src/lint/post-synth/fixtures/TF032/negative-references.tf +214 -0
  258. package/src/lint/post-synth/fixtures/TF032/negative.tf +99 -0
  259. package/src/lint/post-synth/fixtures/TF032/positive-heredoc.tf +22 -0
  260. package/src/lint/post-synth/fixtures/TF032/positive-sidecar.tf +197 -0
  261. package/src/lint/post-synth/fixtures/TF032/positive.tf +54 -0
  262. package/src/lint/post-synth/fixtures/TF033/negative.tf +39 -0
  263. package/src/lint/post-synth/fixtures/TF033/not-determined-aurora.tf +25 -0
  264. package/src/lint/post-synth/fixtures/TF033/positive-absent.tf +93 -0
  265. package/src/lint/post-synth/fixtures/TF033/positive.tf +50 -0
  266. package/src/lint/post-synth/fixtures/TF034/negative.tf +24 -0
  267. package/src/lint/post-synth/fixtures/TF034/positive-absent.tf +36 -0
  268. package/src/lint/post-synth/fixtures/TF034/positive.tf +14 -0
  269. package/src/lint/post-synth/fixtures/TF035/negative-absent.tf +43 -0
  270. package/src/lint/post-synth/fixtures/TF035/negative.tf +54 -0
  271. package/src/lint/post-synth/fixtures/TF035/positive.tf +37 -0
  272. package/src/lint/post-synth/fixtures/TF036/negative.tf +61 -0
  273. package/src/lint/post-synth/fixtures/TF036/not-determined.tf +28 -0
  274. package/src/lint/post-synth/fixtures/TF036/positive.tf +38 -0
  275. package/src/lint/post-synth/fixtures/TF037/negative-exclusion.tf +63 -0
  276. package/src/lint/post-synth/fixtures/TF037/negative.tf +27 -0
  277. package/src/lint/post-synth/fixtures/TF037/not-determined.tf +27 -0
  278. package/src/lint/post-synth/fixtures/TF037/positive-absent.tf +15 -0
  279. package/src/lint/post-synth/fixtures/TF037/positive.tf +26 -0
  280. package/src/lint/post-synth/fixtures/TF038/positive.tf +3 -0
  281. package/src/lint/post-synth/fixtures/TF039/positive.tf +4 -0
  282. package/src/lint/post-synth/fixtures/TF041/negative/terragrunt.hcl +12 -0
  283. package/src/lint/post-synth/fixtures/TF041/positive/terragrunt.hcl +7 -0
  284. package/src/lint/post-synth/fixtures/TF041/positive-apply/terragrunt.hcl +8 -0
  285. package/src/lint/post-synth/fixtures/TF042/negative/terragrunt.hcl +13 -0
  286. package/src/lint/post-synth/fixtures/TF042/positive/terragrunt.hcl +9 -0
  287. package/src/lint/post-synth/fixtures/TF043/negative/terragrunt.hcl +3 -0
  288. package/src/lint/post-synth/fixtures/TF043/negative-other/terragrunt.hcl +3 -0
  289. package/src/lint/post-synth/fixtures/TF043/positive-git/terragrunt.hcl +3 -0
  290. package/src/lint/post-synth/fixtures/TF043/positive-oci/terragrunt.hcl +3 -0
  291. package/src/lint/post-synth/fixtures/TF043/positive-tfr/terragrunt.hcl +3 -0
  292. package/src/lint/post-synth/fixtures/TF044/negative/terragrunt.hcl +9 -0
  293. package/src/lint/post-synth/fixtures/TF044/positive/terragrunt.hcl +7 -0
  294. package/src/lint/post-synth/fixtures/TF044/positive-generate/terragrunt.hcl +9 -0
  295. package/src/lint/post-synth/fixtures/TF045/negative/root.hcl +1 -0
  296. package/src/lint/post-synth/fixtures/TF045/negative/terragrunt.hcl +3 -0
  297. package/src/lint/post-synth/fixtures/TF045/positive/root.hcl +9 -0
  298. package/src/lint/post-synth/fixtures/TF045/positive/terragrunt.hcl +3 -0
  299. package/src/lint/post-synth/fixtures/TF045/unit-only/terragrunt.hcl +3 -0
  300. package/src/lint/post-synth/fixtures/estate-2284/clean/main.tf +132 -0
  301. package/src/lint/post-synth/fixtures/estate-2284/violating/main.tf +112 -0
  302. package/src/lint/post-synth/flat-attributes.ts +146 -0
  303. package/src/lint/post-synth/index.ts +32 -0
  304. package/src/lint/post-synth/module-source.test.ts +42 -1
  305. package/src/lint/post-synth/module-source.ts +56 -1
  306. package/src/lint/post-synth/terragrunt.ts +76 -0
  307. package/src/lint/post-synth/tf002.ts +1 -1
  308. package/src/lint/post-synth/tf030.test.ts +219 -0
  309. package/src/lint/post-synth/tf030.ts +290 -0
  310. package/src/lint/post-synth/tf031.test.ts +229 -0
  311. package/src/lint/post-synth/tf031.ts +370 -0
  312. package/src/lint/post-synth/tf032.test.ts +127 -0
  313. package/src/lint/post-synth/tf032.ts +226 -0
  314. package/src/lint/post-synth/tf033.test.ts +126 -0
  315. package/src/lint/post-synth/tf033.ts +160 -0
  316. package/src/lint/post-synth/tf034.test.ts +71 -0
  317. package/src/lint/post-synth/tf034.ts +63 -0
  318. package/src/lint/post-synth/tf035.test.ts +68 -0
  319. package/src/lint/post-synth/tf035.ts +74 -0
  320. package/src/lint/post-synth/tf036.test.ts +87 -0
  321. package/src/lint/post-synth/tf036.ts +119 -0
  322. package/src/lint/post-synth/tf037.test.ts +88 -0
  323. package/src/lint/post-synth/tf037.ts +101 -0
  324. package/src/lint/post-synth/tf038.test.ts +51 -0
  325. package/src/lint/post-synth/tf038.ts +71 -0
  326. package/src/lint/post-synth/tf039.test.ts +40 -0
  327. package/src/lint/post-synth/tf039.ts +64 -0
  328. package/src/lint/post-synth/tf040.test.ts +55 -0
  329. package/src/lint/post-synth/tf040.ts +76 -0
  330. package/src/lint/post-synth/tf041.test.ts +50 -0
  331. package/src/lint/post-synth/tf041.ts +56 -0
  332. package/src/lint/post-synth/tf042.test.ts +19 -0
  333. package/src/lint/post-synth/tf042.ts +42 -0
  334. package/src/lint/post-synth/tf043.test.ts +65 -0
  335. package/src/lint/post-synth/tf043.ts +49 -0
  336. package/src/lint/post-synth/tf044.test.ts +27 -0
  337. package/src/lint/post-synth/tf044.ts +51 -0
  338. package/src/lint/post-synth/tf045.test.ts +30 -0
  339. package/src/lint/post-synth/tf045.ts +47 -0
  340. package/src/op/activities/index.ts +4 -0
  341. package/src/op/activities/pin-rollout.ts +77 -0
  342. package/src/op/activities/terraform.test.ts +13 -0
  343. package/src/op/activities/terraform.ts +46 -86
  344. package/src/op/activity-contracts.test.ts +4 -1
  345. package/src/op/activity-contracts.ts +42 -0
  346. package/src/op/builders.ts +13 -0
  347. package/src/pin/bundle.test.ts +53 -0
  348. package/src/pin/edit-ts.test.ts +39 -0
  349. package/src/pin/edit-ts.ts +77 -0
  350. package/src/pin/edit.test.ts +199 -0
  351. package/src/pin/edit.ts +157 -0
  352. package/src/pin/forge.ts +85 -0
  353. package/src/pin/index.ts +22 -0
  354. package/src/pin/rollout.test.ts +232 -0
  355. package/src/pin/rollout.ts +451 -0
  356. package/src/pin/scan.ts +309 -0
  357. package/src/pin/source.ts +206 -0
  358. package/src/pin/waves.test.ts +101 -0
  359. package/src/pin/waves.ts +125 -0
  360. package/src/plan-digest.ts +99 -0
  361. package/src/plan-summary.test.ts +154 -0
  362. package/src/plugin.ts +25 -2
  363. package/src/terragrunt/affected.acceptance.test.ts +83 -0
  364. package/src/terragrunt/affected.test.ts +177 -0
  365. package/src/terragrunt/affected.ts +348 -0
  366. package/src/terragrunt/index.ts +11 -0
  367. package/src/terragrunt/mocks.acceptance.test.ts +105 -0
  368. package/src/terragrunt/mocks.test.ts +309 -0
  369. package/src/terragrunt/mocks.ts +324 -0
  370. package/src/terragrunt/run.ts +369 -0
  371. package/src/terragrunt/terragrunt.acceptance.test.ts +122 -0
  372. package/src/terragrunt/terragrunt.test.ts +332 -0
  373. package/src/terragrunt/units.ts +235 -0
  374. package/src/terragrunt/wave.ts +181 -0
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Byte offsets for the attribute values the pin edit rewrites (#3189).
3
+ *
4
+ * `@cdktn/hcl2json` is the reader that says what a file means, and the pin
5
+ * edit asks it first (`./edit.ts`). It gives back values and no positions
6
+ * (`../hcl/value.ts` says why), so it cannot say WHERE in the file a
7
+ * `source` or `version` literal sits. This lexer answers only that: it walks
8
+ * the raw text token by token, skipping comments, quoted strings, template
9
+ * interpolations and heredocs, counts braces, and records each top-level
10
+ * block and the plain string literal of each of its own attributes, with the
11
+ * literal's offsets.
12
+ *
13
+ * It is an HCL tokenizer, not a pattern match over lines: a `source =` inside
14
+ * a comment, a heredoc or another string is never a match, and a block opened
15
+ * on the same line as its attributes reads the same as one split over lines.
16
+ * What it finds is checked against the reader before anything is written:
17
+ * the literal's unescaped value has to equal what hcl2json read for that
18
+ * attribute, and the edited file has to parse to the same tree with only the
19
+ * pinned values changed.
20
+ */
21
+ /** One plain string literal: the offsets of its content, quotes excluded. */
22
+ export interface ScannedLiteral {
23
+ start: number;
24
+ end: number;
25
+ /** The content as written, escapes included. */
26
+ raw: string;
27
+ }
28
+ /** One attribute of a block body. `literal` is set when the value is a single string with no interpolation. */
29
+ export interface ScannedAttribute {
30
+ name: string;
31
+ literal?: ScannedLiteral;
32
+ }
33
+ /** One top-level block: `module "vpc" { ... }`, `terraform { ... }`. */
34
+ export interface ScannedBlock {
35
+ type: string;
36
+ labels: string[];
37
+ /** Offset of the block's type keyword. */
38
+ start: number;
39
+ /** Offset just past the closing brace. */
40
+ end: number;
41
+ /** The block body's own attributes, in file order. Nested blocks' attributes are not included. */
42
+ attributes: ScannedAttribute[];
43
+ }
44
+ /** Thrown when the text is not well-formed enough to place an edit in. */
45
+ export declare class HclScanError extends Error {
46
+ constructor(file: string, message: string);
47
+ }
48
+ /** Every top-level block in `text`, with its own attributes' literals. */
49
+ export declare function scanBlocks(text: string, file: string): ScannedBlock[];
50
+ /** Undo HCL's string escapes, so a literal's raw text compares to the value the reader returned. */
51
+ export declare function unescapeHcl(raw: string): string;
52
+ /** Write a value as the content of an HCL string literal. */
53
+ export declare function escapeHcl(value: string): string;
54
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/pin/scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,+GAA+G;AAC/G,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,kGAAkG;IAClG,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAYD,0EAA0E;AAC1E,qBAAa,YAAa,SAAQ,KAAK;IACrC,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAGxC;CACF;AAmID,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,CAgCrE;AAkDD,oGAAoG;AACpG,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAyB/C;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Where a module call keeps its pin, and how the pin moves (#3189).
3
+ *
4
+ * A module call names a module and, if it is pinned, one exact version of it.
5
+ * The version sits in one of two places:
6
+ *
7
+ * - the `version` argument, beside a registry `source`
8
+ * (`source = "app.terraform.io/acme/vpc/aws"`, `version = "1.3.0"`);
9
+ * - a query argument of the `source` itself: OpenTofu's `oci://` sources take
10
+ * `?tag=` or `?digest=`, a git source takes `?ref=`, and Terragrunt's
11
+ * `tfr://` source takes `?version=`.
12
+ *
13
+ * The module's identity is the source with the pin taken out, so
14
+ * `oci://r.example.com/m/vpc?tag=1.3.0` and `oci://r.example.com/m/vpc?tag=1.4.0`
15
+ * call the same module.
16
+ *
17
+ * A `version` constraint (`~> 1.4`, `>= 1.4, < 2`) is not a pin. An upstream
18
+ * release changes what such a root runs with no diff to review, and there is
19
+ * no single version to move, so the call is refused with that reason. The
20
+ * same holds for a call with no pin at all.
21
+ *
22
+ * OpenTofu writes an OCI pin as a query argument
23
+ * (https://opentofu.org/docs/language/modules/sources/#oci-distribution-registries),
24
+ * not as a `:tag` or `@sha256:` suffix on the repository, so the query form is
25
+ * the one read here.
26
+ *
27
+ * This module is pure string work over values the HCL reader has already
28
+ * parsed. Finding those values in a file, and writing the new ones back, is
29
+ * `./edit.ts`. #3190 adds an `oci` kind to the lint classifier
30
+ * (`../lint/post-synth/module-source.ts`) with the same parse; the two can
31
+ * share one once both have landed.
32
+ */
33
+ /** The query arguments that carry a pin. */
34
+ export type PinParam = "tag" | "digest" | "ref" | "version";
35
+ /** What a module call pins, read from its `source` and `version`. */
36
+ export interface ModuleCallPin {
37
+ /** The module the call names: its `source` with the pin argument taken out. */
38
+ module: string;
39
+ /** Where the pin is: the `version` argument, or a query argument of `source`. */
40
+ at: "version" | "source";
41
+ /** The query argument holding the pin, when `at` is `source`. */
42
+ param?: PinParam;
43
+ /** The pinned version, tag, digest or ref. Null when there is no pin to move. */
44
+ pin: string | null;
45
+ /** Why the call has no pin to move, when `pin` is null. */
46
+ unpinned?: string;
47
+ }
48
+ /** The outcome of moving one call's pin. */
49
+ export type PinMove = {
50
+ outcome: "moved";
51
+ from: string;
52
+ to: string;
53
+ source: string;
54
+ version?: string;
55
+ } | {
56
+ outcome: "already";
57
+ pin: string;
58
+ } | {
59
+ outcome: "elsewhere";
60
+ pin: string;
61
+ } | {
62
+ outcome: "refused";
63
+ reason: string;
64
+ };
65
+ /**
66
+ * The exact version a `version` argument pins, or null when it is a
67
+ * constraint. `1.4.0` and `= 1.4.0` are exact; `~> 1.4`, `>= 1.4` and
68
+ * `1.4.0, < 2` are not.
69
+ */
70
+ export declare function exactVersion(constraint: string): string | null;
71
+ /** Whether a value can be a pin's new value: an exact version, a tag, a ref or a digest, never a constraint. */
72
+ export declare function isPinValue(value: string): boolean;
73
+ /** Read the pin a module call carries. `version` is the call's `version` argument, when it has one. */
74
+ export declare function readModulePin(source: string, version?: string): ModuleCallPin;
75
+ /**
76
+ * Move a call's pin from `from` to `to`, when the call names `module`.
77
+ * Returns null when it names another module. `version` keeps any `=` it was
78
+ * written with, so `= 1.3.0` becomes `= 1.4.0`.
79
+ */
80
+ export declare function movePin(source: string, version: string | undefined, request: {
81
+ module: string;
82
+ from: string;
83
+ to: string;
84
+ }): PinMove | null;
85
+ /** The module a source names, with any pin taken out, so a caller may name the module by a pinned source. */
86
+ export declare function moduleOf(source: string): string;
87
+ /** Refuse a request whose `to` is not something a pin can hold. */
88
+ export declare function checkPinRequest(request: {
89
+ module: string;
90
+ from: string;
91
+ to: string;
92
+ }): void;
93
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/pin/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,4CAA4C;AAC5C,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;AAE5D,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,EAAE,EAAE,SAAS,GAAG,QAAQ,CAAC;IACzB,iEAAiE;IACjE,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,iFAAiF;IACjF,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,4CAA4C;AAC5C,MAAM,MAAM,OAAO,GACf;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAChF;IAAE,OAAO,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,OAAO,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAQ3C;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG9D;AAED,gHAAgH;AAChH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD;AA6CD,uGAAuG;AACvG,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa,CAO7E;AA4CD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,GAAG,IAAI,CAa1I;AAED,6GAA6G;AAC7G,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAM3F"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The wave plan for a pin-bump rollout (#3189): which roots move in which PR.
3
+ *
4
+ * Two sources give the same shape, an ordered list of root lists:
5
+ *
6
+ * - **Declared canaries, then dependency order.** The canaries form wave 1.
7
+ * Every other root lands in the earliest wave after every root it depends
8
+ * on. That is the Kahn layering `chant components fan-out` uses
9
+ * (`resolveComponentGraph`, `@intentius/chant/components/layers`), and the
10
+ * order #3049 gives its gated waves. A canary that depends on a root outside
11
+ * the canaries is refused, since wave 1 would apply before what it reads.
12
+ * - **choudoufu's wave planner** (`choudoufu live-waves -json`, choudoufu#1754).
13
+ * It reads cross-estate references from the estates' own configuration, puts
14
+ * `-canary` roots in wave 1, and emits each wave's roots on their own, so
15
+ * {@link wavesFromChoudoufu} takes its document as it is.
16
+ *
17
+ * A root's dependencies come from the caller (`dependsOn`) and, for a
18
+ * Terragrunt unit, from its `dependency` and `dependencies` blocks
19
+ * ({@link terragruntDependencies}). A dependency on a root outside the
20
+ * rollout orders nothing: it is not moving.
21
+ */
22
+ import type { Hcl2Json } from "@intentius/chant/terraform/hcl2json";
23
+ /** One root in a rollout. `root` is its directory, relative to the repository, with `/` separators. */
24
+ export interface PinRoot {
25
+ root: string;
26
+ /** Roots this one reads, by directory. They land in an earlier wave. */
27
+ dependsOn?: string[];
28
+ /**
29
+ * The TypeScript file a generated root is built from. The pin edit then
30
+ * goes to the module declaration there rather than to the `.tf` chant
31
+ * writes (`./edit-ts.ts`).
32
+ */
33
+ tsSource?: string;
34
+ }
35
+ /** One wave: its number from 1, whether the declared canaries form it, and its roots, sorted. */
36
+ export interface PinWave {
37
+ wave: number;
38
+ canary: boolean;
39
+ roots: string[];
40
+ }
41
+ /** Thrown when the roots cannot be split into waves. The message names the roots. */
42
+ export declare class PinWavePlanError extends Error {
43
+ constructor(message: string);
44
+ }
45
+ /** Canaries first, then dependency order. */
46
+ export declare function planPinWaves(roots: readonly PinRoot[], canaries?: readonly string[]): PinWave[];
47
+ /**
48
+ * The waves of a `choudoufu live-waves -json` document (format version 1).
49
+ * `prefix` is the directory choudoufu ran in, relative to the repository,
50
+ * when its roots are named relative to that rather than to the repository.
51
+ */
52
+ export declare function wavesFromChoudoufu(doc: unknown, prefix?: string): PinWave[];
53
+ /**
54
+ * Keep only the roots in `keep`, renumbering the waves and dropping any left
55
+ * empty. A wave plan from choudoufu covers every root it was given; the
56
+ * rollout moves only the roots that call the module at the old or new pin.
57
+ */
58
+ export declare function restrictWaves(waves: readonly PinWave[], keep: ReadonlySet<string>): PinWave[];
59
+ /**
60
+ * The roots a Terragrunt unit depends on: each `dependency` block's
61
+ * `config_path` and the `dependencies` block's `paths`, resolved against the
62
+ * unit's directory. A path that is an expression is skipped, since it names
63
+ * no directory until Terragrunt evaluates it.
64
+ */
65
+ export declare function terragruntDependencies(root: string, text: string, parser: Hcl2Json): Promise<string[]>;
66
+ //# sourceMappingURL=waves.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waves.d.ts","sourceRoot":"","sources":["../../src/pin/waves.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAGpE,uGAAuG;AACvG,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,iGAAiG;AACjG,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,qFAAqF;AACrF,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,6CAA6C;AAC7C,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,QAAQ,GAAE,SAAS,MAAM,EAAO,GAAG,OAAO,EAAE,CAqBnG;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,OAAO,EAAE,CAUvE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,EAAE,CAO7F;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAS5G"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * A terraform plan's identity (#2300), kept apart from the activities so the
3
+ * change-set adapter (`../change-set.ts`, #3181) can import it without the
4
+ * config loader, the HCL parse or anything else the activities reach. It
5
+ * imports chant's `lifecycle/plan-digest` subpath and nothing more, which is
6
+ * what lets terragucci bundle it into one file (#3421).
7
+ */
8
+ /**
9
+ * One resource's proposed change, as a plan digest sees it (#2300) — the
10
+ * projection of a `terraform show -json` `resource_changes` entry that
11
+ * survives into {@link terraformPlanDigest}.
12
+ */
13
+ export interface TerraformPlannedChange {
14
+ address: unknown;
15
+ mode: unknown;
16
+ type: unknown;
17
+ name: unknown;
18
+ index: unknown;
19
+ provider_name: unknown;
20
+ deposed: unknown;
21
+ actions: unknown;
22
+ before: unknown;
23
+ after: unknown;
24
+ after_unknown: unknown;
25
+ replace_paths: unknown;
26
+ importing: unknown;
27
+ }
28
+ /**
29
+ * The change set a `terraform show -json` plan describes, canonicalised
30
+ * (#2300): every proposed change, at its address, with the values that would
31
+ * be written, sorted so two renderings of the same plan project identically.
32
+ *
33
+ * Everything else in the document is dropped, and the drops are the point —
34
+ * see `packages/core/src/lifecycle/plan-digest.ts` for the general rule. Here
35
+ * that means `timestamp` and `terraform_version` (when the plan was taken and
36
+ * by which binary), `format_version` (how it was rendered), `prior_state` and
37
+ * `planned_values` (restatements of the same change set from another angle),
38
+ * `configuration` (the source that produced it, which can be refactored
39
+ * without changing a single proposed action) and `checks` / `relevant_
40
+ * attributes` (diagnostics about the plan, not the plan). What is left is
41
+ * what applying it would do.
42
+ */
43
+ export declare function terraformPlanChangeSet(planJson: unknown): {
44
+ resourceChanges: TerraformPlannedChange[];
45
+ outputChanges: unknown;
46
+ actionInvocations?: unknown[];
47
+ };
48
+ /**
49
+ * The identity of a terraform plan (#2300): {@link terraformPlanChangeSet}
50
+ * hashed. This is what a `TerraformApplyOp` gate binds its approval to, and
51
+ * what a later run recomputes and compares.
52
+ *
53
+ * Taken over the `show -json` rendering rather than the plan file's bytes on
54
+ * purpose: the file is an opaque, version-stamped archive, and two runs of
55
+ * `plan -out` over an unchanged root produce different bytes. The rendering
56
+ * is the same plan both times.
57
+ */
58
+ export declare function terraformPlanDigest(planJson: unknown): string;
59
+ //# sourceMappingURL=plan-digest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-digest.d.ts","sourceRoot":"","sources":["../src/plan-digest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,OAAO,GAAG;IACzD,eAAe,EAAE,sBAAsB,EAAE,CAAC;IAC1C,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,EAAE,CAAC;CAC/B,CAkCA;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAE7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAgElF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,aAiQ7B,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAmElF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,aAqR7B,CAAC"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Shared reading helpers for TF033-TF037, the five flat encryption and
3
+ * immutability rules (chant #2288, epic #2284).
4
+ *
5
+ * Each of those rules reads one or two scalar attributes of one resource type
6
+ * and answers a three-valued question: the literal proves the resource
7
+ * secure, the literal proves it insecure, or the parse cannot tell. This
8
+ * module is the reading half; each rule file holds its own answer for
9
+ * absence, because that answer differs per resource (see each rule's doc
10
+ * comment and its section of `docs/pages/lint-rules.mdx`). It exports no
11
+ * `PostSynthCheck`, so the generated barrel skips it.
12
+ */
13
+
14
+ import type { PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
15
+ import { RESOURCE_TYPE, type BlockBody } from "../../hcl/parse";
16
+ import { attr } from "../../hcl/value";
17
+ import { blocksOfType, nestedBodies, type TerraformBlock } from "./blocks";
18
+
19
+ /** What one attribute read settles. `reason` says why a value could not be read. */
20
+ export type Read<T> = { kind: "known"; value: T } | { kind: "absent" } | { kind: "unknown"; reason: string };
21
+
22
+ /** Every `resource` block of one provider type (`aws_sqs_queue`), in entity order. */
23
+ export function resourcesOfType(ctx: PostSynthContext, type: string): TerraformBlock[] {
24
+ return blocksOfType(ctx, RESOURCE_TYPE).filter((b) => b.address.startsWith(`${type}.`));
25
+ }
26
+
27
+ /** One expression, for a message: `var.encrypted`, or the template text. */
28
+ function describe(name: string, raw: unknown, refs: string[] | undefined, kind: "reference" | "template"): string {
29
+ return kind === "reference"
30
+ ? `\`${name}\` is an expression (${refs?.[0] ?? String(raw)})`
31
+ : `\`${name}\` is a template string ("${String(raw)}")`;
32
+ }
33
+
34
+ /**
35
+ * A bool attribute. Terraform converts the strings `"true"` and `"false"` to
36
+ * bool, so those read as the bool they convert to. Any other literal (a
37
+ * number, a list) is a plan-time type error, not this rule's concern, and
38
+ * reads as unknown.
39
+ */
40
+ export function readBool(body: BlockBody, name: string): Read<boolean> {
41
+ const a = attr(body, name);
42
+ if (a.kind === "absent") return { kind: "absent" };
43
+ if (a.kind === "reference" || a.kind === "template") {
44
+ return { kind: "unknown", reason: describe(name, a.raw, a.refs, a.kind) };
45
+ }
46
+ if (typeof a.value === "boolean") return { kind: "known", value: a.value };
47
+ if (a.value === "true" || a.value === "false") return { kind: "known", value: a.value === "true" };
48
+ return { kind: "unknown", reason: `\`${name}\` is not a bool (${JSON.stringify(a.value)})` };
49
+ }
50
+
51
+ /** A string attribute, literal or not. */
52
+ export function readString(body: BlockBody, name: string): Read<string> {
53
+ const a = attr(body, name);
54
+ if (a.kind === "absent") return { kind: "absent" };
55
+ if (a.kind === "reference" || a.kind === "template") {
56
+ return { kind: "unknown", reason: describe(name, a.raw, a.refs, a.kind) };
57
+ }
58
+ if (typeof a.value === "string") return { kind: "known", value: a.value };
59
+ return { kind: "unknown", reason: `\`${name}\` is not a string (${JSON.stringify(a.value)})` };
60
+ }
61
+
62
+ /** Expression roots that name an input or a computed value, not a managed object. */
63
+ const VALUE_ROOTS = new Set(["var", "local", "module", "each", "count", "path", "terraform", "self"]);
64
+
65
+ /** `aws_kms_key.sns.arn`, `data.aws_kms_alias.sqs.target_key_arn`, with an optional index or splat. */
66
+ const OBJECT_ATTRIBUTE = /^(?:data\.)?([a-z][a-z0-9_]*)\.[A-Za-z_][\w-]*(?:\[[^\]]*\])?(?:\.[A-Za-z_][\w-]*|\[[^\]]*\])+$/;
67
+
68
+ /** Every `${...}` span in a template, as `attr()` finds them. */
69
+ const SPANS = /\$\{(?:[^{}]|\{[^{}]*\})*\}/g;
70
+
71
+ /**
72
+ * Whether a key-like argument (`kms_master_key_id`) is set to something.
73
+ *
74
+ * - absent, or `null`: `absent`.
75
+ * - a literal: `known`, with the string (`""` is a value, and a rule decides
76
+ * what an empty key means).
77
+ * - an attribute of a managed resource or data source (`aws_kms_key.x.arn`):
78
+ * `known` and non-empty. The provider computes a key's id or ARN; it is
79
+ * never the empty string, so the key is set whatever its value turns out to
80
+ * be.
81
+ * - a template with literal text (`"alias/${var.env}-sns"`): `known` and
82
+ * non-empty, for the same reason.
83
+ * - anything else (`var.kms_key`, `local.key`, a conditional, a function
84
+ * call): `unknown`. A variable can be `null` or `""`.
85
+ */
86
+ export function readKeySet(body: BlockBody, name: string): Read<string> {
87
+ const a = attr(body, name);
88
+ if (a.kind === "absent") return { kind: "absent" };
89
+ if (a.kind === "literal") {
90
+ return typeof a.value === "string"
91
+ ? { kind: "known", value: a.value }
92
+ : { kind: "unknown", reason: `\`${name}\` is not a string (${JSON.stringify(a.value)})` };
93
+ }
94
+ const raw = String(a.raw);
95
+ if (a.kind === "template") {
96
+ if (raw.replace(SPANS, "").length > 0) return { kind: "known", value: raw };
97
+ return { kind: "unknown", reason: describe(name, a.raw, a.refs, "template") };
98
+ }
99
+ const expr = (a.refs?.[0] ?? "").trim();
100
+ const m = OBJECT_ATTRIBUTE.exec(expr);
101
+ if (m && !VALUE_ROOTS.has(m[1])) return { kind: "known", value: raw };
102
+ return { kind: "unknown", reason: describe(name, a.raw, a.refs, "reference") };
103
+ }
104
+
105
+ /**
106
+ * Whether an argument that names a source (`snapshot_identifier`,
107
+ * `replicate_source_db`) or a nested block (`restore_to_point_in_time`) is
108
+ * given. The provider reads these with `GetOk`, so a literal `""` is the same
109
+ * as leaving the argument out; anything else, a reference included, is given.
110
+ */
111
+ export function isGiven(body: BlockBody, name: string): boolean {
112
+ const a = attr(body, name);
113
+ if (a.kind === "absent") return false;
114
+ if (a.kind === "literal") {
115
+ if (a.value === "" || a.value === false) return false;
116
+ if (Array.isArray(a.value)) return a.value.length > 0;
117
+ }
118
+ return true;
119
+ }
120
+
121
+ /** The bodies of a nested block, re-exported for the rule files. */
122
+ export { nestedBodies };
123
+
124
+ /** A finding the literal proves. */
125
+ export function finding(
126
+ checkId: string,
127
+ severity: "error" | "warning",
128
+ block: TerraformBlock,
129
+ message: string,
130
+ ): PostSynthDiagnostic {
131
+ return { checkId, severity, message: `"${block.address}" ${message}`, entity: block.key, lexicon: "terraform" };
132
+ }
133
+
134
+ /**
135
+ * The one `info` diagnostic a rule emits when it cannot read the value it
136
+ * needs. The message starts `Not determined:`, as #2284 fixed for TF030-TF037.
137
+ */
138
+ export function notDetermined(checkId: string, block: TerraformBlock, reason: string, tail: string): PostSynthDiagnostic {
139
+ return {
140
+ checkId,
141
+ severity: "info",
142
+ message: `Not determined: "${block.address}" ${reason}. ${tail}`,
143
+ entity: block.key,
144
+ lexicon: "terraform",
145
+ };
146
+ }
@@ -18,14 +18,27 @@
18
18
  * recognize, scp-style `git@host:path` addresses, and any source ending in
19
19
  * `.git`. `local` is `./` or `../` (checkov's `UNKNOWN` case for module
20
20
  * pinning: a local path cannot be pinned to anything and is never flagged).
21
+ * `oci` is an OpenTofu `oci://host/repo` source (#3190). OpenTofu selects a
22
+ * version with `?tag=` or `?digest=` (https://opentofu.org/docs/language/modules/sources/),
23
+ * and a source naming neither resolves the `latest` tag. The container-image
24
+ * shapes `repo:tag` and `repo@sha256:...` are read too, so a source written
25
+ * the way `docker pull` spells it is not misread as unpinned. A `:` inside the
26
+ * host (a registry port) is never a tag: only a colon in the last path
27
+ * segment is. The tag and digest rules were checked against the docs page
28
+ * above; the suffix forms are not documented there and are not verified
29
+ * against OpenTofu's parser.
21
30
  */
22
31
 
23
- export type ModuleSourceKind = "local" | "registry" | "git" | "other";
32
+ export type ModuleSourceKind = "local" | "registry" | "git" | "oci" | "other";
24
33
 
25
34
  export interface ModuleSourceClassification {
26
35
  kind: ModuleSourceKind;
27
36
  /** The `?ref=`/`&ref=` value (falling back to `rev`), when `kind` is `"git"`. */
28
37
  ref?: string;
38
+ /** The tag an `oci://` source names, from `?tag=` or a `:tag` suffix on the repository. */
39
+ tag?: string;
40
+ /** The digest an `oci://` source names, from `?digest=` or an `@sha256:...` suffix. */
41
+ digest?: string;
29
42
  }
30
43
 
31
44
  const REGISTRY_NAME = /^[0-9A-Za-z](?:[0-9A-Za-z_-]{0,62}[0-9A-Za-z])?$/;
@@ -77,6 +90,25 @@ function isGitShaped(rest: string): boolean {
77
90
  return false;
78
91
  }
79
92
 
93
+ function classifyOci(base: string, query: string): ModuleSourceClassification {
94
+ const params = parseQuery(query);
95
+ let path = stripSubdir(base).replace(/^oci:\/\//i, "");
96
+ let tag = params.get("tag") || undefined;
97
+ let digest = params.get("digest") || undefined;
98
+
99
+ const at = path.indexOf("@");
100
+ if (at !== -1) {
101
+ digest = digest ?? (path.slice(at + 1) || undefined);
102
+ path = path.slice(0, at);
103
+ }
104
+ const lastSlash = path.lastIndexOf("/");
105
+ const colon = path.indexOf(":", lastSlash + 1); // a port sits before the first slash
106
+ if (colon !== -1) {
107
+ tag = tag ?? (path.slice(colon + 1) || undefined);
108
+ }
109
+ return { kind: "oci", ...(tag !== undefined ? { tag } : {}), ...(digest !== undefined ? { digest } : {}) };
110
+ }
111
+
80
112
  export function classifyModuleSource(source: string): ModuleSourceClassification {
81
113
  const trimmed = source.trim();
82
114
  if (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith(".\\") || trimmed.startsWith("..\\")) {
@@ -87,6 +119,8 @@ export function classifyModuleSource(source: string): ModuleSourceClassification
87
119
  const base = queryIdx === -1 ? trimmed : trimmed.slice(0, queryIdx);
88
120
  const query = queryIdx === -1 ? "" : trimmed.slice(queryIdx + 1);
89
121
 
122
+ if (/^oci:\/\//i.test(base)) return classifyOci(base, query);
123
+
90
124
  const forced = /^(git|hg)::/i.exec(base);
91
125
  const rest = forced ? base.slice(forced[0].length) : base;
92
126
 
@@ -119,3 +153,24 @@ export function isPinnedRef(ref: string): boolean {
119
153
  export function isDefaultBranch(ref: string): boolean {
120
154
  return DEFAULT_BRANCHES.has(ref);
121
155
  }
156
+
157
+ const MUTABLE_TAGS = new Set(["latest", "main", "master", "develop", "dev", "stable", "edge", "nightly"]);
158
+
159
+ /**
160
+ * Is `tag` a name that conventionally moves (`latest`, a branch-like name)
161
+ * rather than an exact version. Used by TF038. A tag is a mutable pointer in
162
+ * most registries whatever it looks like, so the rule is a warning and this
163
+ * only decides what reads as obviously floating.
164
+ */
165
+ export function isMutableOciTag(tag: string): boolean {
166
+ return MUTABLE_TAGS.has(tag.toLowerCase());
167
+ }
168
+
169
+ /**
170
+ * Is a registry `version` constraint an exact version (`1.4.0`, `= 1.4.0`,
171
+ * `=1.4.0`) rather than a range (`~> 1.4`, `>= 1.4`, `>= 1.0, < 2.0`). Used
172
+ * by TF039. A bare version is an exact constraint in Terraform.
173
+ */
174
+ export function isExactVersionConstraint(version: string): boolean {
175
+ return /^(?:=\s*)?v?\d+(?:\.\d+){0,2}(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(version.trim()) && !version.includes(",");
176
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Shared reading for the Terragrunt rules TF041 to TF045 (#3417).
3
+ *
4
+ * These rules look at `terragrunt.hcl` and `root.hcl`, which the parse reads
5
+ * into their own entity types (`TERRAGRUNT_*_TYPE` in `../../hcl/parse.ts`)
6
+ * and never into the module types the other TF rules check. Like the module
7
+ * source rules, they read the literal values hcl2json hands back: an
8
+ * attribute built from a function or a `local.` reference arrives as a
9
+ * `"${...}"` string and is treated as not determined, never as a finding.
10
+ */
11
+
12
+ import { isResourceDeclarable, type Declarable } from "@intentius/chant/declarable";
13
+ import type { BlockBody } from "../../hcl/parse";
14
+ import { classifyModuleSource } from "./module-source";
15
+
16
+ /** Entities of one Terragrunt type, with their keys, in map order. */
17
+ export function terragruntEntities(
18
+ entities: ReadonlyMap<string, Declarable>,
19
+ entityType: string,
20
+ ): Array<{ key: string; address: string; file: string; body: BlockBody }> {
21
+ const out: Array<{ key: string; address: string; file: string; body: BlockBody }> = [];
22
+ for (const [key, entity] of entities) {
23
+ if (entity.entityType !== entityType || !isResourceDeclarable(entity)) continue;
24
+ const props = entity.props as { address?: unknown; file?: unknown; body?: unknown };
25
+ out.push({
26
+ key,
27
+ address: typeof props.address === "string" ? props.address : key,
28
+ file: typeof props.file === "string" ? props.file : "terragrunt.hcl",
29
+ body: (typeof props.body === "object" && props.body !== null ? props.body : {}) as BlockBody,
30
+ });
31
+ }
32
+ return out;
33
+ }
34
+
35
+ /** An HCL expression hcl2json left as `"${...}"`: its value is not known without evaluating it. */
36
+ export function isExpression(value: unknown): boolean {
37
+ return typeof value === "string" && value.includes("${");
38
+ }
39
+
40
+ export type TerragruntSourceKind = "local" | "git" | "tfr" | "oci" | "other";
41
+
42
+ export interface TerragruntSourceClassification {
43
+ kind: TerragruntSourceKind;
44
+ /** Whether the source names a version: `?ref=` for git, `?version=` for `tfr://`, a tag or digest for `oci://`. Undefined for `local` and `other`. */
45
+ pinned?: boolean;
46
+ }
47
+
48
+ /**
49
+ * Classify a unit's `terraform.source`. Git and `oci://` sources go through
50
+ * {@link classifyModuleSource}, the reader TF005 and TF038 use. `tfr://` is
51
+ * Terragrunt's own scheme for registry modules and takes its version from the
52
+ * `?version=` query argument. A source that holds an interpolation, or that
53
+ * names an absolute path or an archive URL, is `other` or `local` and is
54
+ * never reported.
55
+ */
56
+ export function classifyTerragruntSource(source: string): TerragruntSourceClassification {
57
+ const trimmed = source.trim();
58
+ if (trimmed.includes("${")) return { kind: "other" };
59
+ if (/^tfr:\/\//i.test(trimmed)) {
60
+ const query = trimmed.includes("?") ? trimmed.slice(trimmed.indexOf("?") + 1) : "";
61
+ const version = new URLSearchParams(query).get("version");
62
+ return { kind: "tfr", pinned: version !== null && version.trim() !== "" };
63
+ }
64
+ if (trimmed.startsWith("/")) return { kind: "local" };
65
+ const classified = classifyModuleSource(trimmed);
66
+ switch (classified.kind) {
67
+ case "local":
68
+ return { kind: "local" };
69
+ case "git":
70
+ return { kind: "git", pinned: classified.ref !== undefined };
71
+ case "oci":
72
+ return { kind: "oci", pinned: classified.tag !== undefined || classified.digest !== undefined };
73
+ default:
74
+ return { kind: "other" };
75
+ }
76
+ }
@@ -51,7 +51,7 @@ function providerNameFromType(type: string): string {
51
51
  }
52
52
 
53
53
  /** One implied provider name to the entity key that first implied it in file order. */
54
- function impliedProviders(entities: PostSynthContext["entities"], root: string): Map<string, string> {
54
+ export function impliedProviders(entities: PostSynthContext["entities"], root: string): Map<string, string> {
55
55
  const implied = new Map<string, string>();
56
56
  const fromProviderBlock = new Set<string>();
57
57