@intentius/chant-lexicon-terraform 0.124.54 → 0.124.56

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 (351) hide show
  1. package/README.md +7 -4
  2. package/dist/ci-setup.d.ts +11 -0
  3. package/dist/ci-setup.d.ts.map +1 -0
  4. package/dist/ci-setup.js +41 -0
  5. package/dist/components/capability-plugin.d.ts +40 -6
  6. package/dist/components/capability-plugin.d.ts.map +1 -1
  7. package/dist/components/capability-plugin.js +174 -30
  8. package/dist/components/compose-group.d.ts +1 -1
  9. package/dist/components/compose-group.d.ts.map +1 -1
  10. package/dist/components/compose-group.js +1 -1
  11. package/dist/components/release-inputs.d.ts +29 -0
  12. package/dist/components/release-inputs.d.ts.map +1 -0
  13. package/dist/components/release-inputs.js +57 -0
  14. package/dist/components/trace-facts.d.ts +16 -0
  15. package/dist/components/trace-facts.d.ts.map +1 -0
  16. package/dist/components/trace-facts.js +19 -0
  17. package/dist/cone.d.ts +53 -0
  18. package/dist/cone.d.ts.map +1 -0
  19. package/dist/cone.js +119 -0
  20. package/dist/cost-hold.d.ts +27 -0
  21. package/dist/cost-hold.d.ts.map +1 -0
  22. package/dist/cost-hold.js +21 -0
  23. package/dist/drift-rows.d.ts +61 -0
  24. package/dist/drift-rows.d.ts.map +1 -0
  25. package/dist/drift-rows.js +108 -0
  26. package/dist/exit-codes.d.ts +55 -0
  27. package/dist/exit-codes.d.ts.map +1 -0
  28. package/dist/exit-codes.js +48 -0
  29. package/dist/index.d.ts +2 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +3 -0
  32. package/dist/integrity.json +7 -4
  33. package/dist/lint/audit-catalog.d.ts.map +1 -1
  34. package/dist/lint/audit-catalog.js +27 -3
  35. package/dist/lint/audit-lineage.d.ts +14 -2
  36. package/dist/lint/audit-lineage.d.ts.map +1 -1
  37. package/dist/lint/post-synth/index.d.ts.map +1 -1
  38. package/dist/lint/post-synth/index.js +4 -0
  39. package/dist/lint/post-synth/record-store.d.ts +54 -0
  40. package/dist/lint/post-synth/record-store.d.ts.map +1 -0
  41. package/dist/lint/post-synth/record-store.js +104 -0
  42. package/dist/lint/post-synth/tf001.d.ts +2 -2
  43. package/dist/lint/post-synth/tf001.js +2 -2
  44. package/dist/lint/post-synth/tf024.d.ts +35 -11
  45. package/dist/lint/post-synth/tf024.d.ts.map +1 -1
  46. package/dist/lint/post-synth/tf024.js +92 -20
  47. package/dist/lint/post-synth/tf046.d.ts +64 -0
  48. package/dist/lint/post-synth/tf046.d.ts.map +1 -0
  49. package/dist/lint/post-synth/tf046.js +175 -0
  50. package/dist/lint/post-synth/tf047.d.ts +36 -0
  51. package/dist/lint/post-synth/tf047.d.ts.map +1 -0
  52. package/dist/lint/post-synth/tf047.js +94 -0
  53. package/dist/lock-rows.d.ts +36 -0
  54. package/dist/lock-rows.d.ts.map +1 -0
  55. package/dist/lock-rows.js +60 -0
  56. package/dist/lock-timeout.d.ts +22 -0
  57. package/dist/lock-timeout.d.ts.map +1 -0
  58. package/dist/lock-timeout.js +32 -0
  59. package/dist/manifest.json +1 -1
  60. package/dist/module-catalog/index.d.ts +69 -0
  61. package/dist/module-catalog/index.d.ts.map +1 -0
  62. package/dist/module-catalog/index.js +110 -0
  63. package/dist/module-catalog/refresh-cli.d.ts +4 -0
  64. package/dist/module-catalog/refresh-cli.d.ts.map +1 -0
  65. package/dist/module-catalog/refresh-cli.js +64 -0
  66. package/dist/module-catalog/static-tier.d.ts +6 -0
  67. package/dist/module-catalog/static-tier.d.ts.map +1 -0
  68. package/dist/module-catalog/static-tier.js +1650 -0
  69. package/dist/module-catalog/types.d.ts +65 -0
  70. package/dist/module-catalog/types.d.ts.map +1 -0
  71. package/dist/module-catalog/types.js +33 -0
  72. package/dist/okf/index.md +3 -1
  73. package/dist/okf/rules/TF024.md +2 -2
  74. package/dist/okf/rules/TF046.md +11 -0
  75. package/dist/okf/rules/TF047.md +11 -0
  76. package/dist/op/activities/terraform.d.ts +29 -0
  77. package/dist/op/activities/terraform.d.ts.map +1 -1
  78. package/dist/op/activities/terraform.js +19 -4
  79. package/dist/pin/consumers.d.ts +35 -0
  80. package/dist/pin/consumers.d.ts.map +1 -0
  81. package/dist/pin/consumers.js +70 -0
  82. package/dist/plugin.d.ts.map +1 -1
  83. package/dist/plugin.js +5 -0
  84. package/dist/registry/address.d.ts +63 -0
  85. package/dist/registry/address.d.ts.map +1 -0
  86. package/dist/registry/address.js +65 -0
  87. package/dist/registry/catalog-read.d.ts +14 -0
  88. package/dist/registry/catalog-read.d.ts.map +1 -0
  89. package/dist/registry/catalog-read.js +58 -0
  90. package/dist/registry/deprecated.d.ts +37 -0
  91. package/dist/registry/deprecated.d.ts.map +1 -0
  92. package/dist/registry/deprecated.js +43 -0
  93. package/dist/registry/docs.d.ts +96 -0
  94. package/dist/registry/docs.d.ts.map +1 -0
  95. package/dist/registry/docs.js +210 -0
  96. package/dist/registry/edge/edge.d.ts +46 -0
  97. package/dist/registry/edge/edge.d.ts.map +1 -0
  98. package/dist/registry/edge/edge.js +194 -0
  99. package/dist/registry/edge/index.d.ts +11 -0
  100. package/dist/registry/edge/index.d.ts.map +1 -0
  101. package/dist/registry/edge/index.js +10 -0
  102. package/dist/registry/edge/lambda-edge.d.ts +76 -0
  103. package/dist/registry/edge/lambda-edge.d.ts.map +1 -0
  104. package/dist/registry/edge/lambda-edge.js +95 -0
  105. package/dist/registry/edge/sigv4-web.d.ts +33 -0
  106. package/dist/registry/edge/sigv4-web.d.ts.map +1 -0
  107. package/dist/registry/edge/sigv4-web.js +56 -0
  108. package/dist/registry/edge/token-source.d.ts +73 -0
  109. package/dist/registry/edge/token-source.d.ts.map +1 -0
  110. package/dist/registry/edge/token-source.js +115 -0
  111. package/dist/registry/edge/worker.d.ts +45 -0
  112. package/dist/registry/edge/worker.d.ts.map +1 -0
  113. package/dist/registry/edge/worker.js +26 -0
  114. package/dist/registry/handler.d.ts +25 -0
  115. package/dist/registry/handler.d.ts.map +1 -0
  116. package/dist/registry/handler.js +75 -0
  117. package/dist/registry/headers-file.d.ts +21 -0
  118. package/dist/registry/headers-file.d.ts.map +1 -0
  119. package/dist/registry/headers-file.js +57 -0
  120. package/dist/registry/index.d.ts +31 -0
  121. package/dist/registry/index.d.ts.map +1 -0
  122. package/dist/registry/index.js +30 -0
  123. package/dist/registry/mirror-paths.d.ts +9 -0
  124. package/dist/registry/mirror-paths.d.ts.map +1 -0
  125. package/dist/registry/mirror-paths.js +8 -0
  126. package/dist/registry/mirror.d.ts +130 -0
  127. package/dist/registry/mirror.d.ts.map +1 -0
  128. package/dist/registry/mirror.js +319 -0
  129. package/dist/registry/proxies.d.ts +73 -0
  130. package/dist/registry/proxies.d.ts.map +1 -0
  131. package/dist/registry/proxies.js +111 -0
  132. package/dist/registry/reader.d.ts +57 -0
  133. package/dist/registry/reader.d.ts.map +1 -0
  134. package/dist/registry/reader.js +198 -0
  135. package/dist/registry/semver.d.ts +16 -0
  136. package/dist/registry/semver.d.ts.map +1 -0
  137. package/dist/registry/semver.js +24 -0
  138. package/dist/registry/serve.d.ts +24 -0
  139. package/dist/registry/serve.d.ts.map +1 -0
  140. package/dist/registry/serve.js +64 -0
  141. package/dist/registry/source-forms.d.ts +79 -0
  142. package/dist/registry/source-forms.d.ts.map +1 -0
  143. package/dist/registry/source-forms.js +202 -0
  144. package/dist/registry/targets/artifactory.d.ts +28 -0
  145. package/dist/registry/targets/artifactory.d.ts.map +1 -0
  146. package/dist/registry/targets/artifactory.js +206 -0
  147. package/dist/registry/targets/fake-artifactory.d.ts +30 -0
  148. package/dist/registry/targets/fake-artifactory.d.ts.map +1 -0
  149. package/dist/registry/targets/fake-artifactory.js +142 -0
  150. package/dist/registry/targets/fake-gitlab.d.ts +39 -0
  151. package/dist/registry/targets/fake-gitlab.d.ts.map +1 -0
  152. package/dist/registry/targets/fake-gitlab.js +136 -0
  153. package/dist/registry/targets/fake-hcp.d.ts +50 -0
  154. package/dist/registry/targets/fake-hcp.d.ts.map +1 -0
  155. package/dist/registry/targets/fake-hcp.js +202 -0
  156. package/dist/registry/targets/fake-http.d.ts +31 -0
  157. package/dist/registry/targets/fake-http.d.ts.map +1 -0
  158. package/dist/registry/targets/fake-http.js +87 -0
  159. package/dist/registry/targets/fake-nexus.d.ts +27 -0
  160. package/dist/registry/targets/fake-nexus.d.ts.map +1 -0
  161. package/dist/registry/targets/fake-nexus.js +137 -0
  162. package/dist/registry/targets/gitlab.d.ts +53 -0
  163. package/dist/registry/targets/gitlab.d.ts.map +1 -0
  164. package/dist/registry/targets/gitlab.js +187 -0
  165. package/dist/registry/targets/hcp.d.ts +29 -0
  166. package/dist/registry/targets/hcp.d.ts.map +1 -0
  167. package/dist/registry/targets/hcp.js +310 -0
  168. package/dist/registry/targets/hosted.d.ts +62 -0
  169. package/dist/registry/targets/hosted.d.ts.map +1 -0
  170. package/dist/registry/targets/hosted.js +74 -0
  171. package/dist/registry/targets/index.d.ts +18 -0
  172. package/dist/registry/targets/index.d.ts.map +1 -0
  173. package/dist/registry/targets/index.js +17 -0
  174. package/dist/registry/targets/module-target.d.ts +68 -0
  175. package/dist/registry/targets/module-target.d.ts.map +1 -0
  176. package/dist/registry/targets/module-target.js +63 -0
  177. package/dist/registry/targets/nexus.d.ts +43 -0
  178. package/dist/registry/targets/nexus.d.ts.map +1 -0
  179. package/dist/registry/targets/nexus.js +250 -0
  180. package/dist/registry/targets/zip.d.ts +55 -0
  181. package/dist/registry/targets/zip.d.ts.map +1 -0
  182. package/dist/registry/targets/zip.js +278 -0
  183. package/dist/registry/writer.d.ts +101 -0
  184. package/dist/registry/writer.d.ts.map +1 -0
  185. package/dist/registry/writer.js +109 -0
  186. package/dist/rules/record-store.ts +122 -0
  187. package/dist/rules/tf001.ts +2 -2
  188. package/dist/rules/tf024.ts +105 -22
  189. package/dist/rules/tf046.ts +204 -0
  190. package/dist/rules/tf047.ts +114 -0
  191. package/dist/state/azure-blob.d.ts +8 -67
  192. package/dist/state/azure-blob.d.ts.map +1 -1
  193. package/dist/state/azure-blob.js +6 -199
  194. package/dist/state/gcs.d.ts +7 -61
  195. package/dist/state/gcs.d.ts.map +1 -1
  196. package/dist/state/gcs.js +5 -222
  197. package/dist/state/object-store.d.ts +4 -33
  198. package/dist/state/object-store.d.ts.map +1 -1
  199. package/dist/state/object-store.js +4 -17
  200. package/dist/state/s3.d.ts +11 -58
  201. package/dist/state/s3.d.ts.map +1 -1
  202. package/dist/state/s3.js +10 -161
  203. package/dist/state/store.js +2 -2
  204. package/dist/state-ops.d.ts +255 -0
  205. package/dist/state-ops.d.ts.map +1 -0
  206. package/dist/state-ops.js +305 -0
  207. package/dist/state-reads.d.ts +57 -0
  208. package/dist/state-reads.d.ts.map +1 -0
  209. package/dist/state-reads.js +104 -0
  210. package/dist/used-sources.d.ts +54 -0
  211. package/dist/used-sources.d.ts.map +1 -0
  212. package/dist/used-sources.js +157 -0
  213. package/package.json +19 -3
  214. package/src/__fixtures__/drift-record/README.md +5 -0
  215. package/src/__fixtures__/drift-rows/live-root-plan.json +70 -0
  216. package/src/__fixtures__/drift-rows/refresh-plan.json +61 -0
  217. package/src/__fixtures__/used-sources/root/extra.tofu +1 -0
  218. package/src/__fixtures__/used-sources/root/main.tf +31 -0
  219. package/src/__fixtures__/used-sources/root/modules/net/main.tf +14 -0
  220. package/src/__fixtures__/used-sources/root/modules/net/versions.tf.json +7 -0
  221. package/src/__fixtures__/used-sources/unit/terragrunt.hcl +3 -0
  222. package/src/ci-setup.ts +42 -0
  223. package/src/components/capability-plugin.test.ts +26 -14
  224. package/src/components/capability-plugin.ts +185 -31
  225. package/src/components/compose-group.ts +1 -1
  226. package/src/components/pr-loop-compose.tofu.e2e.test.ts +1 -1
  227. package/src/components/pr-loop.tofu.test.ts +1 -1
  228. package/src/components/release-inputs.ts +69 -0
  229. package/src/components/trace-facts.test.ts +21 -0
  230. package/src/components/trace-facts.ts +27 -0
  231. package/src/cone.ts +138 -0
  232. package/src/cost-hold.ts +29 -0
  233. package/src/describe-resources.engine-facts.test.ts +2 -3
  234. package/src/drift-rows.test.ts +135 -0
  235. package/src/drift-rows.ts +137 -0
  236. package/src/exit-codes.test.ts +23 -0
  237. package/src/exit-codes.ts +59 -0
  238. package/src/index.ts +3 -0
  239. package/src/lint/audit-catalog.ts +29 -3
  240. package/src/lint/audit-lineage.ts +14 -2
  241. package/src/lint/post-synth/fixtures/TF024/live-with-record-store/main.tf +18 -0
  242. package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/estate.chdf.hcl +1 -0
  243. package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/main.tf +10 -0
  244. package/src/lint/post-synth/fixtures/TF046/azurerm-no-resource-group/main.tf +15 -0
  245. package/src/lint/post-synth/fixtures/TF046/backend-config-only/main.tf +14 -0
  246. package/src/lint/post-synth/fixtures/TF046/derived-s3/backend.tf +7 -0
  247. package/src/lint/post-synth/fixtures/TF046/derived-s3/main.tf +9 -0
  248. package/src/lint/post-synth/fixtures/TF046/explicit-local/main.tf +16 -0
  249. package/src/lint/post-synth/fixtures/TF046/explicit-s3-beside-local-backend/main.tf +15 -0
  250. package/src/lint/post-synth/fixtures/TF046/implied-local/main.tf +9 -0
  251. package/src/lint/post-synth/fixtures/TF046/sidecar-implied/estate.chdf.hcl +1 -0
  252. package/src/lint/post-synth/fixtures/TF046/sidecar-implied/main.tf +5 -0
  253. package/src/lint/post-synth/fixtures/TF047/clear/main.tf +12 -0
  254. package/src/lint/post-synth/fixtures/TF047/refused/main.tf +12 -0
  255. package/src/lint/post-synth/fixtures/TF047/unknown-module/main.tf +12 -0
  256. package/src/lint/post-synth/fixtures/TF047/unknown-version/main.tf +12 -0
  257. package/src/lint/post-synth/index.ts +4 -0
  258. package/src/lint/post-synth/record-store.ts +122 -0
  259. package/src/lint/post-synth/tf001.ts +2 -2
  260. package/src/lint/post-synth/tf024.test.ts +26 -5
  261. package/src/lint/post-synth/tf024.ts +105 -22
  262. package/src/lint/post-synth/tf046.test.ts +137 -0
  263. package/src/lint/post-synth/tf046.ts +204 -0
  264. package/src/lint/post-synth/tf047.test.ts +56 -0
  265. package/src/lint/post-synth/tf047.ts +114 -0
  266. package/src/lock-rows.test.ts +70 -0
  267. package/src/lock-rows.ts +82 -0
  268. package/src/lock-timeout.ts +34 -0
  269. package/src/module-catalog/index.test.ts +93 -0
  270. package/src/module-catalog/index.ts +159 -0
  271. package/src/module-catalog/refresh-cli.ts +68 -0
  272. package/src/module-catalog/static-tier.ts +1659 -0
  273. package/src/module-catalog/types.ts +88 -0
  274. package/src/op/activities/terraform.ts +46 -5
  275. package/src/op-wave-step.test.ts +109 -0
  276. package/src/pin/__fixtures__/consumers/live/dev/main.tf +4 -0
  277. package/src/pin/__fixtures__/consumers/live/prod/main.tf +21 -0
  278. package/src/pin/__fixtures__/consumers/units/vpc/terragrunt.hcl +7 -0
  279. package/src/pin/consumers.test.ts +57 -0
  280. package/src/pin/consumers.ts +95 -0
  281. package/src/plugin.ts +6 -0
  282. package/src/registry/__fixtures__/docs/documented/.terraform-docs.yml +3 -0
  283. package/src/registry/__fixtures__/docs/documented/README.md +1 -0
  284. package/src/registry/__fixtures__/docs/documented/main.tf +8 -0
  285. package/src/registry/__fixtures__/docs/json/main.tf.json +14 -0
  286. package/src/registry/__fixtures__/docs/vpc/README.md +3 -0
  287. package/src/registry/__fixtures__/docs/vpc/examples/complete/main.tf +5 -0
  288. package/src/registry/__fixtures__/docs/vpc/examples/simple/README.md +1 -0
  289. package/src/registry/__fixtures__/docs/vpc/examples/simple/main.tf +4 -0
  290. package/src/registry/__fixtures__/docs/vpc/main.tf +14 -0
  291. package/src/registry/__fixtures__/docs/vpc/outputs.tf +9 -0
  292. package/src/registry/__fixtures__/docs/vpc/variables.tf +16 -0
  293. package/src/registry/__fixtures__/proxied-root/main.tf +12 -0
  294. package/src/registry/address.ts +100 -0
  295. package/src/registry/catalog-read.ts +56 -0
  296. package/src/registry/deprecated.test.ts +139 -0
  297. package/src/registry/deprecated.ts +76 -0
  298. package/src/registry/docs.test.ts +140 -0
  299. package/src/registry/docs.ts +299 -0
  300. package/src/registry/edge/bundle.test.ts +50 -0
  301. package/src/registry/edge/edge.test.ts +176 -0
  302. package/src/registry/edge/edge.ts +211 -0
  303. package/src/registry/edge/index.ts +10 -0
  304. package/src/registry/edge/lambda-edge.ts +127 -0
  305. package/src/registry/edge/sigv4-web.ts +79 -0
  306. package/src/registry/edge/token-source.test.ts +258 -0
  307. package/src/registry/edge/token-source.ts +169 -0
  308. package/src/registry/edge/worker.ts +61 -0
  309. package/src/registry/handler.ts +89 -0
  310. package/src/registry/headers-file.ts +61 -0
  311. package/src/registry/index.ts +31 -0
  312. package/src/registry/mirror-paths.ts +10 -0
  313. package/src/registry/mirror.test.ts +198 -0
  314. package/src/registry/mirror.ts +395 -0
  315. package/src/registry/proxies.test.ts +153 -0
  316. package/src/registry/proxies.ts +132 -0
  317. package/src/registry/reader.ts +225 -0
  318. package/src/registry/registry.test.ts +262 -0
  319. package/src/registry/semver.ts +34 -0
  320. package/src/registry/serve.ts +82 -0
  321. package/src/registry/source-forms.test.ts +87 -0
  322. package/src/registry/source-forms.ts +216 -0
  323. package/src/registry/targets/artifactory.test.ts +248 -0
  324. package/src/registry/targets/artifactory.ts +257 -0
  325. package/src/registry/targets/fake-artifactory.ts +158 -0
  326. package/src/registry/targets/fake-gitlab.ts +161 -0
  327. package/src/registry/targets/fake-hcp.ts +233 -0
  328. package/src/registry/targets/fake-http.ts +97 -0
  329. package/src/registry/targets/fake-nexus.ts +152 -0
  330. package/src/registry/targets/gitlab.test.ts +208 -0
  331. package/src/registry/targets/gitlab.ts +223 -0
  332. package/src/registry/targets/hcp.test.ts +230 -0
  333. package/src/registry/targets/hcp.ts +360 -0
  334. package/src/registry/targets/hosted.ts +117 -0
  335. package/src/registry/targets/index.ts +17 -0
  336. package/src/registry/targets/module-target.ts +131 -0
  337. package/src/registry/targets/nexus.test.ts +220 -0
  338. package/src/registry/targets/nexus.ts +314 -0
  339. package/src/registry/targets/zip.test.ts +65 -0
  340. package/src/registry/targets/zip.ts +287 -0
  341. package/src/registry/writer.ts +155 -0
  342. package/src/state/azure-blob.ts +6 -221
  343. package/src/state/gcs.ts +5 -252
  344. package/src/state/object-store.ts +4 -32
  345. package/src/state/s3.ts +10 -182
  346. package/src/state/store.ts +2 -2
  347. package/src/state-ops.test.ts +182 -0
  348. package/src/state-ops.ts +462 -0
  349. package/src/state-reads.ts +116 -0
  350. package/src/used-sources.test.ts +75 -0
  351. package/src/used-sources.ts +204 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The files a release records for a `terraform-apply` step (chant#4542).
3
+ *
4
+ * The step names its root by key, so core can't see which files it deploys.
5
+ * `terraformReleaseInputs` answers core's `releaseInputs` with the set a
6
+ * pull request's plan treats as the root's change (`rootWatchPaths`): the
7
+ * root's directory, each local module it reaches, and its var files, plus
8
+ * its backend-config files. A release cut records them for the component,
9
+ * and a promote or rollback under the release writes them under
10
+ * `.chant/release-templates/<digest>/` and sets the step's `dir` to the copy
11
+ * of the root, so `terraform-apply` plans the release's configuration against
12
+ * the environment's state.
13
+ *
14
+ * A root whose state is a file in its own directory (the `local` backend) is
15
+ * unbound: a plan run in a copy elsewhere would read no state and propose
16
+ * creating everything. Such a root rolls back through `chant lifecycle
17
+ * rollback --to`. A live choudoufu root has no state file and is recorded.
18
+ */
19
+ import { dirname, relative, resolve } from "node:path";
20
+ import { loadChantConfigUpward } from "@intentius/chant/config";
21
+ import { rootWatchPaths } from "../changed-roots.js";
22
+ import { liveEstate } from "../compose/hcl.js";
23
+ import { readBackend } from "../state/backend.js";
24
+ const toPosix = (p) => p.split("\\").join("/") || ".";
25
+ /** What a release records for one `terraform-apply` step, relative to the member's directory. Null when the root is not declared. */
26
+ export async function terraformReleaseInputs(input, ctx) {
27
+ if (typeof input.root !== "string" || input.root === "")
28
+ return null;
29
+ const start = resolve(ctx.memberDir, input.cwd ?? ".");
30
+ const { config, configPath } = await loadChantConfigUpward(start);
31
+ const projectRoot = configPath ? dirname(configPath) : start;
32
+ const terraform = config.terraform;
33
+ const root = terraform?.roots?.[input.root];
34
+ if (!root)
35
+ return null;
36
+ const binary = terraform?.binary;
37
+ const rootDir = resolve(projectRoot, input.dir ?? root.dir);
38
+ const live = binary === "choudoufu" && (root.estate !== undefined || liveEstate(rootDir, binary).live);
39
+ if (!live) {
40
+ const { backend } = readBackend(rootDir, {
41
+ ...(root.backendConfig ? { backendConfig: root.backendConfig } : {}),
42
+ ...(root.backendConfigFiles ? { backendConfigFiles: root.backendConfigFiles } : {}),
43
+ ...(binary ? { binary } : {}),
44
+ });
45
+ if (backend.type === "local") {
46
+ return {
47
+ unbound: `root "${input.root}" keeps its state in a local file under ${toPosix(relative(ctx.memberDir, rootDir))}, ` +
48
+ "so a plan of a recorded copy would read no state; roll it back with chant lifecycle rollback --to <ref>",
49
+ };
50
+ }
51
+ }
52
+ const fromMember = (abs) => toPosix(relative(ctx.memberDir, abs));
53
+ const paths = rootWatchPaths(projectRoot, { dir: relative(projectRoot, rootDir) || ".", ...(root.varFiles ? { varFiles: root.varFiles } : {}) }, binary).map((p) => fromMember(resolve(projectRoot, p)));
54
+ for (const file of root.backendConfigFiles ?? [])
55
+ paths.push(fromMember(resolve(rootDir, file)));
56
+ return { field: "dir", dir: fromMember(rootDir), paths: [...new Set(paths)].sort() };
57
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `terraform-apply`'s engine facts for its span and op-wave run record
3
+ * (INTENTIUS/chant#4555): the root, the binary that applied it, and whether
4
+ * it applied the plan the wave decided. Core adds the plan's resource counts
5
+ * by action, and writes every key under the CI product's prefix
6
+ * (`terragucci.root`, `terragucci.binary`).
7
+ */
8
+ import type { CapabilityTraceFacts } from "@intentius/chant/otlp";
9
+ import type { TerraformApplyStepInput, TerraformApplyStepOutput } from "./capability-plugin.js";
10
+ /** `traceFacts` for `terraform-apply` (#4555). `binary` is the config's `terraform.binary`, `terraform` when it names none. */
11
+ export declare function terraformTraceFacts(input: TerraformApplyStepInput, outcome: {
12
+ output: TerraformApplyStepOutput;
13
+ } | {
14
+ error: unknown;
15
+ }, binary: string | undefined): CapabilityTraceFacts;
16
+ //# sourceMappingURL=trace-facts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-facts.d.ts","sourceRoot":"","sources":["../../src/components/trace-facts.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE7F,+HAA+H;AAC/H,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,uBAAuB,EAC9B,OAAO,EAAE;IAAE,MAAM,EAAE,wBAAwB,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,EAClE,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,oBAAoB,CAUtB"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `terraform-apply`'s engine facts for its span and op-wave run record
3
+ * (INTENTIUS/chant#4555): the root, the binary that applied it, and whether
4
+ * it applied the plan the wave decided. Core adds the plan's resource counts
5
+ * by action, and writes every key under the CI product's prefix
6
+ * (`terragucci.root`, `terragucci.binary`).
7
+ */
8
+ /** `traceFacts` for `terraform-apply` (#4555). `binary` is the config's `terraform.binary`, `terraform` when it names none. */
9
+ export function terraformTraceFacts(input, outcome, binary) {
10
+ const bin = (binary ?? "terraform").split(/[\\/]/).pop() || "terraform";
11
+ return {
12
+ attributes: {
13
+ root: input.root,
14
+ binary: bin,
15
+ ...("output" in outcome && outcome.output ? { "apply.from_wave_plan": outcome.output.fromWavePlan, "apply.outputs": Object.keys(outcome.output.outputs ?? {}).length } : {}),
16
+ ...(input.cone ? { "plan.cone": input.cone } : {}),
17
+ },
18
+ };
19
+ }
package/dist/cone.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ /** The flags choudoufu refuses beside `-cone`, or plans in full with: a plan that carries one gets no `-cone`. */
2
+ export declare const CONE_EXCLUDES: readonly ["-target", "-exclude", "-destroy", "-refresh-only", "-json", "-estate"];
3
+ /** What choudoufu writes beside a saved cone plan, `FILE.cone.json`. */
4
+ export interface ConeRecord {
5
+ base: string;
6
+ base_sha: string;
7
+ targets: string[];
8
+ }
9
+ /** What one root's cone plan did: planned the cone, or planned the whole estate for the guard's reason. */
10
+ export interface PlanCone {
11
+ /** The revision the plan was asked to measure the change from. */
12
+ base: string;
13
+ /** With a cone: its blocks, and the estate's, as choudoufu counted them. Absent when it did not say. */
14
+ blocks?: number;
15
+ of?: number;
16
+ /** With a cone: the record beside the plan file. */
17
+ record?: ConeRecord;
18
+ /** The guard planned the whole estate: its kind and why. */
19
+ fallback?: {
20
+ kind: string;
21
+ reason: string;
22
+ };
23
+ }
24
+ /** Whether `binary` (a name or a path) is choudoufu. */
25
+ export declare const isChoudoufu: (binary: string) => boolean;
26
+ /**
27
+ * The `-cone` argument a plan takes: `-cone=<base>` for choudoufu with a
28
+ * base, on a root that takes it (`live`), when none of `args` is a flag in
29
+ * {@link CONE_EXCLUDES}. Otherwise none, and the plan is full.
30
+ */
31
+ export declare function coneArgs(binary: string, base: string | undefined, args: readonly string[], cone: boolean): string[];
32
+ /** The path of the cone record beside a plan file. */
33
+ export declare const coneRecordPath: (planFile: string) => string;
34
+ /** The cone record beside `planFile`, or none when there is none or it does not read as one. */
35
+ export declare function readConeRecord(planFile: string): ConeRecord | undefined;
36
+ /**
37
+ * What a plan run with `-cone=<base>` did, from its output and the record
38
+ * beside `planFile`. A fallback warning means the whole estate was planned,
39
+ * even after a cone warning. With no fallback, the record is the cone. With
40
+ * neither, choudoufu said nothing this reads, and the plan counts as full.
41
+ */
42
+ export declare function readPlanCone(output: string, planFile: string, base: string): PlanCone;
43
+ /** What a cone plan did, in words: its size, or why it planned the whole estate. */
44
+ export declare function coneText(cone: PlanCone): string;
45
+ /**
46
+ * A root's plan digest with its cone in it: the plan's own digest, unchanged,
47
+ * when the plan was full; with a cone, the digest over the plan's digest and
48
+ * the cone's targets, so an approval of one cone counts for no other. The
49
+ * digest's kind is terragucci's, so approvals recorded before the move still
50
+ * name the same digest.
51
+ */
52
+ export declare function conePlanDigest(planDigest: string, cone: PlanCone | undefined): string;
53
+ //# sourceMappingURL=cone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cone.d.ts","sourceRoot":"","sources":["../src/cone.ts"],"names":[],"mappings":"AA4BA,kHAAkH;AAClH,eAAO,MAAM,aAAa,YAAI,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,OAAO,EAAE,SAAS,CAAU,CAAC;AAE/G,wEAAwE;AACxE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,2GAA2G;AAC3G,MAAM,WAAW,QAAQ;IACvB,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,wGAAwG;IACxG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED,wDAAwD;AACxD,eAAO,MAAM,WAAW,WAAY,MAAM,KAAG,OAA2C,CAAC;AAEzF;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,CAInH;AAED,sDAAsD;AACtD,eAAO,MAAM,cAAc,aAAc,MAAM,KAAG,MAAiC,CAAC;AAEpF,gGAAgG;AAChG,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAUvE;AAsBD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAUrF;AAED,oFAAoF;AACpF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAK/C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAGrF"}
package/dist/cone.js ADDED
@@ -0,0 +1,119 @@
1
+ /**
2
+ * choudoufu's cone plans (#4545, moved from terragucci's `cone.ts`): `plan
3
+ * -cone=<base>` plans only the blocks a change touches and every block that
4
+ * reads them.
5
+ *
6
+ * A wave that applies an open pull request plans a live root as the cone of
7
+ * its change since the pull request's base, as the plan note did. A wave
8
+ * that applies a merge commit and a drift run plan the whole estate, as do
9
+ * a root with no `live` block, an estate file (`estate.chdf.hcl`), whose
10
+ * form choudoufu refuses `-cone` on, and any binary but choudoufu.
11
+ *
12
+ * choudoufu's guard can plan the whole estate anyway (no base, a provider,
13
+ * terraform or live block or the lock file changed, a cone over 30% of the
14
+ * estate's blocks, and so on). It says why in a warning. A cone plan says
15
+ * its size in a warning too, and with `-out FILE` writes `FILE.cone.json`:
16
+ * the cone's base, the commit it named and the addresses it targets.
17
+ *
18
+ * The record's targets join the root's plan digest ({@link conePlanDigest}),
19
+ * so the digest an approval binds names the cone. A wave that plans again
20
+ * after its approval and finds another cone has another digest, and refuses
21
+ * as moved. choudoufu's `apply FILE` checks the same record again.
22
+ *
23
+ * Pure, apart from reading the record beside a plan file.
24
+ */
25
+ import { existsSync, readFileSync } from "node:fs";
26
+ import { basename } from "node:path";
27
+ import { computePlanDigest } from "@intentius/chant/lifecycle/plan-digest";
28
+ /** The flags choudoufu refuses beside `-cone`, or plans in full with: a plan that carries one gets no `-cone`. */
29
+ export const CONE_EXCLUDES = ["-target", "-exclude", "-destroy", "-refresh-only", "-json", "-estate"];
30
+ /** Whether `binary` (a name or a path) is choudoufu. */
31
+ export const isChoudoufu = (binary) => basename(binary) === "choudoufu";
32
+ /**
33
+ * The `-cone` argument a plan takes: `-cone=<base>` for choudoufu with a
34
+ * base, on a root that takes it (`live`), when none of `args` is a flag in
35
+ * {@link CONE_EXCLUDES}. Otherwise none, and the plan is full.
36
+ */
37
+ export function coneArgs(binary, base, args, cone) {
38
+ if (!base || !cone || !isChoudoufu(binary))
39
+ return [];
40
+ const excluded = args.some((a) => CONE_EXCLUDES.some((f) => a === f || a.startsWith(`${f}=`)));
41
+ return excluded ? [] : [`-cone=${base}`];
42
+ }
43
+ /** The path of the cone record beside a plan file. */
44
+ export const coneRecordPath = (planFile) => `${planFile}.cone.json`;
45
+ /** The cone record beside `planFile`, or none when there is none or it does not read as one. */
46
+ export function readConeRecord(planFile) {
47
+ const path = coneRecordPath(planFile);
48
+ if (!existsSync(path))
49
+ return undefined;
50
+ try {
51
+ const r = JSON.parse(readFileSync(path, "utf-8"));
52
+ if (!r || typeof r.base !== "string" || !Array.isArray(r.targets) || !r.targets.every((t) => typeof t === "string"))
53
+ return undefined;
54
+ return { base: r.base, base_sha: typeof r.base_sha === "string" ? r.base_sha : "", targets: [...r.targets].sort() };
55
+ }
56
+ catch {
57
+ return undefined;
58
+ }
59
+ }
60
+ /**
61
+ * The detail of the last warning with this summary in a plan's output, its
62
+ * lines joined. The binary prints a warning as `Warning: <summary>`, a blank
63
+ * line, and the detail up to the next blank line or the plan's summary; a box
64
+ * drawn around it with colour on is stripped.
65
+ */
66
+ function warningDetail(text, summary) {
67
+ const lines = text.replace(/\r\n/g, "\n").split("\n").map((l) => l.replace(/^[\s│╷╵]*/, "").trimEnd());
68
+ let found;
69
+ for (let i = 0; i < lines.length; i++) {
70
+ if (lines[i] !== `Warning: ${summary}`)
71
+ continue;
72
+ let j = i + 1;
73
+ while (j < lines.length && lines[j] === "")
74
+ j++;
75
+ const detail = [];
76
+ for (; j < lines.length && lines[j] !== "" && !/^(Plan: |No changes\.|Changes to Outputs:)/.test(lines[j]); j++)
77
+ detail.push(lines[j]);
78
+ found = detail.join(" ").replace(/\s+/g, " ").trim();
79
+ }
80
+ return found;
81
+ }
82
+ /**
83
+ * What a plan run with `-cone=<base>` did, from its output and the record
84
+ * beside `planFile`. A fallback warning means the whole estate was planned,
85
+ * even after a cone warning. With no fallback, the record is the cone. With
86
+ * neither, choudoufu said nothing this reads, and the plan counts as full.
87
+ */
88
+ export function readPlanCone(output, planFile, base) {
89
+ const fell = warningDetail(output, "Cone plan fell back to a full plan");
90
+ if (fell !== undefined) {
91
+ const m = /^This plan covers the whole estate \(guard ([^)]*)\): (.*?)\.?$/.exec(fell);
92
+ return { base, fallback: m ? { kind: m[1], reason: m[2] } : { kind: "unknown", reason: fell } };
93
+ }
94
+ const record = readConeRecord(planFile);
95
+ if (!record)
96
+ return { base, fallback: { kind: "unknown", reason: "choudoufu wrote no cone record beside the plan" } };
97
+ const size = /: (\d+) of the estate's (\d+) blocks/.exec(warningDetail(output, "Cone plan") ?? "");
98
+ return { base, ...(size ? { blocks: Number(size[1]), of: Number(size[2]) } : {}), record };
99
+ }
100
+ /** What a cone plan did, in words: its size, or why it planned the whole estate. */
101
+ export function coneText(cone) {
102
+ if (cone.fallback)
103
+ return `the whole estate, since ${cone.fallback.reason} (guard ${cone.fallback.kind})`;
104
+ const n = cone.record?.targets.length ?? 0;
105
+ const size = cone.blocks !== undefined && cone.of !== undefined ? `${cone.blocks} of the estate's ${cone.of} blocks` : `${n} target${n === 1 ? "" : "s"}`;
106
+ return `the cone of the change since ${cone.base}, ${size}`;
107
+ }
108
+ /**
109
+ * A root's plan digest with its cone in it: the plan's own digest, unchanged,
110
+ * when the plan was full; with a cone, the digest over the plan's digest and
111
+ * the cone's targets, so an approval of one cone counts for no other. The
112
+ * digest's kind is terragucci's, so approvals recorded before the move still
113
+ * name the same digest.
114
+ */
115
+ export function conePlanDigest(planDigest, cone) {
116
+ if (!cone?.record)
117
+ return planDigest;
118
+ return computePlanDigest("terragucci-cone-plan", { plan: planDigest, targets: [...cone.record.targets].sort() });
119
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The terraform lexicon's preset for an op wave's cost hold (#4545,
3
+ * `holds.cost` on the spec): infracost over each root's plan document, the
4
+ * `show -json` file `terraform-apply`'s plan names as `artifact.planJson`.
5
+ *
6
+ * ```ts
7
+ * const spec = { ..., step: { kind: "terraform-apply", root: "{target}" }, holds: { cost: terraformCostHold({ approveAbove: 100 }) } };
8
+ * ```
9
+ *
10
+ * The shape is core's `OpWaveCostHold`, written out here so the lexicon
11
+ * builds against a core without it.
12
+ */
13
+ /** infracost's own command over one plan document. */
14
+ export declare const INFRACOST_ARGV: readonly ["infracost", "breakdown", "--path", "{planJson}", "--format", "json", "--log-level", "error"];
15
+ export interface TerraformCostHold {
16
+ command: string[];
17
+ approveAbove: number;
18
+ format: "infracost";
19
+ currency?: string;
20
+ }
21
+ /** A cost hold that prices each root with infracost, waiting over `approveAbove` a month. `command` replaces infracost's argv; it must name `{planJson}`. */
22
+ export declare function terraformCostHold(options: {
23
+ approveAbove: number;
24
+ currency?: string;
25
+ command?: string[];
26
+ }): TerraformCostHold;
27
+ //# sourceMappingURL=cost-hold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-hold.d.ts","sourceRoot":"","sources":["../src/cost-hold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,sDAAsD;AACtD,eAAO,MAAM,cAAc,YAAI,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,CAAU,CAAC;AAEtI,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,6JAA6J;AAC7J,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,iBAAiB,CAI7H"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The terraform lexicon's preset for an op wave's cost hold (#4545,
3
+ * `holds.cost` on the spec): infracost over each root's plan document, the
4
+ * `show -json` file `terraform-apply`'s plan names as `artifact.planJson`.
5
+ *
6
+ * ```ts
7
+ * const spec = { ..., step: { kind: "terraform-apply", root: "{target}" }, holds: { cost: terraformCostHold({ approveAbove: 100 }) } };
8
+ * ```
9
+ *
10
+ * The shape is core's `OpWaveCostHold`, written out here so the lexicon
11
+ * builds against a core without it.
12
+ */
13
+ /** infracost's own command over one plan document. */
14
+ export const INFRACOST_ARGV = ["infracost", "breakdown", "--path", "{planJson}", "--format", "json", "--log-level", "error"];
15
+ /** A cost hold that prices each root with infracost, waiting over `approveAbove` a month. `command` replaces infracost's argv; it must name `{planJson}`. */
16
+ export function terraformCostHold(options) {
17
+ const command = options.command ?? [...INFRACOST_ARGV];
18
+ if (!command.some((a) => a.includes("{planJson}")))
19
+ throw new Error("terraformCostHold: the command must name {planJson}, the root's plan document");
20
+ return { command, approveAbove: options.approveAbove, format: "infracost", ...(options.currency ? { currency: options.currency } : {}) };
21
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Drift rows (#4580): one row per object a plan found drifted, by address,
3
+ * with its action and the object's real name, counted the way a product's
4
+ * drift report counts. `./drift-record.ts` answers a different question (the
5
+ * status of each instance the plan names), so it skips create rows, carries
6
+ * no names and stays as it is.
7
+ *
8
+ * A stock root's refresh-only plan reports drift as `resource_drift`, and
9
+ * every entry there is one row, data blocks and creates included. A full
10
+ * plan of a root under live resource markers (`live: true`) reads drift from
11
+ * `resource_changes` instead, seen from the other side: a create there is an
12
+ * object that no longer exists (delete), a delete one that exists outside
13
+ * the code (create), a replacement or any other pair of actions an update.
14
+ * Data blocks, no-op and read entries are not drift.
15
+ *
16
+ * These are terragucci's rules (packages/terragucci/src/report/drift.ts:
17
+ * `liveDrift`, `driftCount`, `driftNames`), so a row count here equals the
18
+ * count its report gives for the same plan.
19
+ *
20
+ * A row carries no attribute values. Its `name` is the first non-empty
21
+ * string among `name`, `id`, `bucket`, `function_name` and `arn`, read from
22
+ * the object as found and then as the code has it; a deleted object has no
23
+ * other place its name survives.
24
+ *
25
+ * The module imports nothing, so a product can load it behind a
26
+ * feature-detect without pulling the lexicon's entry point.
27
+ */
28
+ /** One drifted object. */
29
+ export interface TerraformDriftRow {
30
+ /** The resource address, `""` when the plan's entry has none. */
31
+ address: string;
32
+ /**
33
+ * What happened outside the code: `update`, `delete` (the object is gone),
34
+ * `create` (an object exists that state or code does not hold), or on a
35
+ * stock root whatever single action the plan's entry names. On a stock
36
+ * root a delete-and-create pair is `replace`, and any other pair `update`.
37
+ */
38
+ action: string;
39
+ /** The resource type, when the entry names one. */
40
+ type?: string;
41
+ /** The object's real name, by the rule in this module's header. */
42
+ name?: string;
43
+ }
44
+ export interface TerraformPlanDriftRowsOptions {
45
+ /**
46
+ * The plan is a full plan of a root under live resource markers, whose
47
+ * changes are its drift. Default false: a refresh-only plan's
48
+ * `resource_drift`.
49
+ */
50
+ live?: boolean;
51
+ }
52
+ /**
53
+ * The drift a plan (`show -json`, parsed) found, one row per drifted object
54
+ * in the plan's order. Its length is the drift count.
55
+ */
56
+ export declare function terraformPlanDriftRows(plan: unknown, options?: TerraformPlanDriftRowsOptions): TerraformDriftRow[];
57
+ /** How many objects a plan found drifted: `terraformPlanDriftRows(plan, options).length`. */
58
+ export declare function terraformPlanDriftCount(plan: unknown, options?: TerraformPlanDriftRowsOptions): number;
59
+ /** Each drifted object's real name by address, for the rows that have one. */
60
+ export declare function terraformPlanDriftNames(plan: unknown, options?: TerraformPlanDriftRowsOptions): Map<string, string>;
61
+ //# sourceMappingURL=drift-rows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift-rows.d.ts","sourceRoot":"","sources":["../src/drift-rows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAMH,0BAA0B;AAC1B,MAAM,WAAW,iBAAiB;IAChC,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AA6DD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,6BAAkC,GAAG,iBAAiB,EAAE,CAGtH;AAED,6FAA6F;AAC7F,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,6BAAkC,GAAG,MAAM,CAE1G;AAED,8EAA8E;AAC9E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,6BAAkC,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAIvH"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Drift rows (#4580): one row per object a plan found drifted, by address,
3
+ * with its action and the object's real name, counted the way a product's
4
+ * drift report counts. `./drift-record.ts` answers a different question (the
5
+ * status of each instance the plan names), so it skips create rows, carries
6
+ * no names and stays as it is.
7
+ *
8
+ * A stock root's refresh-only plan reports drift as `resource_drift`, and
9
+ * every entry there is one row, data blocks and creates included. A full
10
+ * plan of a root under live resource markers (`live: true`) reads drift from
11
+ * `resource_changes` instead, seen from the other side: a create there is an
12
+ * object that no longer exists (delete), a delete one that exists outside
13
+ * the code (create), a replacement or any other pair of actions an update.
14
+ * Data blocks, no-op and read entries are not drift.
15
+ *
16
+ * These are terragucci's rules (packages/terragucci/src/report/drift.ts:
17
+ * `liveDrift`, `driftCount`, `driftNames`), so a row count here equals the
18
+ * count its report gives for the same plan.
19
+ *
20
+ * A row carries no attribute values. Its `name` is the first non-empty
21
+ * string among `name`, `id`, `bucket`, `function_name` and `arn`, read from
22
+ * the object as found and then as the code has it; a deleted object has no
23
+ * other place its name survives.
24
+ *
25
+ * The module imports nothing, so a product can load it behind a
26
+ * feature-detect without pulling the lexicon's entry point.
27
+ */
28
+ const isObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
29
+ const FLIP = { create: "delete", delete: "create" };
30
+ const NAME_KEYS = ["name", "id", "bucket", "function_name", "arn"];
31
+ function nameOf(values) {
32
+ const objects = values.filter(isObject);
33
+ for (const key of NAME_KEYS) {
34
+ const v = objects.map((x) => x[key]).find((x) => typeof x === "string" && x !== "");
35
+ if (typeof v === "string")
36
+ return v;
37
+ }
38
+ return undefined;
39
+ }
40
+ function rowOf(entry, action, values) {
41
+ const r = isObject(entry) ? entry : {};
42
+ const name = nameOf(values);
43
+ return {
44
+ address: typeof r.address === "string" ? r.address : "",
45
+ action,
46
+ ...(typeof r.type === "string" && r.type !== "" ? { type: r.type } : {}),
47
+ ...(name !== undefined ? { name } : {}),
48
+ };
49
+ }
50
+ function stockAction(actions) {
51
+ if (actions.length === 1)
52
+ return actions[0];
53
+ if (actions.length === 2 && actions.includes("delete") && actions.includes("create"))
54
+ return "replace";
55
+ return "update";
56
+ }
57
+ /** A refresh-only plan's `resource_drift`, one row per entry. */
58
+ function stockRows(doc) {
59
+ const list = Array.isArray(doc.resource_drift) ? doc.resource_drift : [];
60
+ return list.map((entry) => {
61
+ const ch = isObject(entry) && isObject(entry.change) ? entry.change : {};
62
+ const actions = Array.isArray(ch.actions) ? ch.actions.map(String) : [];
63
+ return rowOf(entry, stockAction(actions), [ch.after, ch.before]);
64
+ });
65
+ }
66
+ /** A live root's full plan, its changes read as drift. */
67
+ function liveRows(doc) {
68
+ const list = Array.isArray(doc.resource_changes) ? doc.resource_changes : [];
69
+ const out = [];
70
+ for (const entry of list) {
71
+ if (!isObject(entry) || entry.mode === "data")
72
+ continue;
73
+ const ch = isObject(entry.change) ? entry.change : {};
74
+ const actions = Array.isArray(ch.actions) ? ch.actions.map(String) : [];
75
+ if (actions.length === 0 || actions.every((a) => a === "no-op" || a === "read"))
76
+ continue;
77
+ const action = actions.length === 1 ? (FLIP[actions[0]] ?? actions[0]) : "update";
78
+ const live = isObject(ch.before) ? ch.before : null;
79
+ let code = isObject(ch.after) ? ch.after : null;
80
+ // A value the plan knows only after the apply is not a difference: the live one stands for it.
81
+ const unknown = isObject(ch.after_unknown) ? ch.after_unknown : {};
82
+ if (code && live)
83
+ code = { ...code, ...Object.fromEntries(Object.keys(unknown).filter((k) => unknown[k] === true && k in live).map((k) => [k, live[k]])) };
84
+ out.push(rowOf(entry, action, [live, code]));
85
+ }
86
+ return out;
87
+ }
88
+ /**
89
+ * The drift a plan (`show -json`, parsed) found, one row per drifted object
90
+ * in the plan's order. Its length is the drift count.
91
+ */
92
+ export function terraformPlanDriftRows(plan, options = {}) {
93
+ if (!isObject(plan))
94
+ return [];
95
+ return options.live ? liveRows(plan) : stockRows(plan);
96
+ }
97
+ /** How many objects a plan found drifted: `terraformPlanDriftRows(plan, options).length`. */
98
+ export function terraformPlanDriftCount(plan, options = {}) {
99
+ return terraformPlanDriftRows(plan, options).length;
100
+ }
101
+ /** Each drifted object's real name by address, for the rows that have one. */
102
+ export function terraformPlanDriftNames(plan, options = {}) {
103
+ const out = new Map();
104
+ for (const row of terraformPlanDriftRows(plan, options))
105
+ if (row.name !== undefined)
106
+ out.set(row.address, row.name);
107
+ return out;
108
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * OpenTofu, Terraform and choudoufu outcomes mapped onto chant's exit-code
3
+ * table (#4553, `CHANT_EXIT` in `@intentius/chant/exit-codes`).
4
+ *
5
+ * The lexicon never picks the process's exit code itself: for a moved plan it
6
+ * throws an error core marks as moved (`planMoved: true`, read by
7
+ * `isPlanMovedError`), and core exits `CHANT_EXIT.moved`. This mapping says
8
+ * which engine outcome is which row of the table, so the throw sites and the
9
+ * reference page agree.
10
+ *
11
+ * | Engine outcome | Table |
12
+ * |---|---|
13
+ * | `plan -detailed-exitcode` 0 or 2 | not an exit: the plan's answer. 2 under a drift check is `drift` |
14
+ * | any 1 | `failed` |
15
+ * | choudoufu `apply <planfile>` 3, approval mismatch | `moved` |
16
+ * | choudoufu `apply <planfile>` 3, plan made against another estate | `failed`: the pipeline handed the wrong file, and no approval fixes that |
17
+ * | choudoufu `live-wave-apply` 3, set moved | `moved` |
18
+ * | choudoufu `live-wave-apply` 4 or `live-plan-set` 4, a root failed | `failed` |
19
+ * | a composed plan that moved | `moved` |
20
+ * | `live-affected` 2, `live-cone` 2 | planner answers, never a job's exit |
21
+ */
22
+ import { type ChantExitCode } from "@intentius/chant/exit-codes";
23
+ import { type TerraformApprovalRefusal } from "./op/activities/terraform.js";
24
+ /** The command whose exit status is mapped. */
25
+ export type TerraformExitSource =
26
+ /** `terraform|tofu|choudoufu plan -detailed-exitcode`; `drift` says a drift check asked for it. */
27
+ {
28
+ command: "plan";
29
+ code: number;
30
+ drift?: boolean;
31
+ }
32
+ /** `choudoufu apply <planfile>` on a live root; `refusal` is `classifyApprovalRefusal`'s answer for its output. */
33
+ | {
34
+ command: "apply";
35
+ code: number;
36
+ refusal?: TerraformApprovalRefusal;
37
+ }
38
+ /** `choudoufu live-wave-apply`. */
39
+ | {
40
+ command: "live-wave-apply";
41
+ code: number;
42
+ }
43
+ /** `choudoufu live-plan-set`. */
44
+ | {
45
+ command: "live-plan-set";
46
+ code: number;
47
+ };
48
+ /**
49
+ * The table's code for a terraform, tofu or choudoufu exit status. A plan's
50
+ * 0 and 2 are both `done` unless a drift check asked, when 2 is `drift`.
51
+ */
52
+ export declare function terraformExitOf(source: TerraformExitSource): ChantExitCode;
53
+ /** Whether `source` is a plan that moved since its approval: the outcomes the lexicon throws a `planMoved` error for. */
54
+ export declare function terraformPlanMoved(source: TerraformExitSource): boolean;
55
+ //# sourceMappingURL=exit-codes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exit-codes.d.ts","sourceRoot":"","sources":["../src/exit-codes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAqD,KAAK,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAE7H,+CAA+C;AAC/C,MAAM,MAAM,mBAAmB;AAC7B,mGAAmG;AACjG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE;AACpD,mHAAmH;GACjH;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,wBAAwB,CAAA;CAAE;AACxE,mCAAmC;GACjC;IAAE,OAAO,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAC9C,iCAAiC;GAC/B;IAAE,OAAO,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,CAc1E;AAED,yHAAyH;AACzH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAEvE"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * OpenTofu, Terraform and choudoufu outcomes mapped onto chant's exit-code
3
+ * table (#4553, `CHANT_EXIT` in `@intentius/chant/exit-codes`).
4
+ *
5
+ * The lexicon never picks the process's exit code itself: for a moved plan it
6
+ * throws an error core marks as moved (`planMoved: true`, read by
7
+ * `isPlanMovedError`), and core exits `CHANT_EXIT.moved`. This mapping says
8
+ * which engine outcome is which row of the table, so the throw sites and the
9
+ * reference page agree.
10
+ *
11
+ * | Engine outcome | Table |
12
+ * |---|---|
13
+ * | `plan -detailed-exitcode` 0 or 2 | not an exit: the plan's answer. 2 under a drift check is `drift` |
14
+ * | any 1 | `failed` |
15
+ * | choudoufu `apply <planfile>` 3, approval mismatch | `moved` |
16
+ * | choudoufu `apply <planfile>` 3, plan made against another estate | `failed`: the pipeline handed the wrong file, and no approval fixes that |
17
+ * | choudoufu `live-wave-apply` 3, set moved | `moved` |
18
+ * | choudoufu `live-wave-apply` 4 or `live-plan-set` 4, a root failed | `failed` |
19
+ * | a composed plan that moved | `moved` |
20
+ * | `live-affected` 2, `live-cone` 2 | planner answers, never a job's exit |
21
+ */
22
+ import { CHANT_EXIT } from "@intentius/chant/exit-codes";
23
+ import { CHOUDOUFU_APPROVAL_EXIT_CODE, CHOUDOUFU_WAVE_EXIT } from "./op/activities/terraform.js";
24
+ /**
25
+ * The table's code for a terraform, tofu or choudoufu exit status. A plan's
26
+ * 0 and 2 are both `done` unless a drift check asked, when 2 is `drift`.
27
+ */
28
+ export function terraformExitOf(source) {
29
+ const { code } = source;
30
+ if (code === 0)
31
+ return CHANT_EXIT.done;
32
+ switch (source.command) {
33
+ case "plan":
34
+ if (code === 2)
35
+ return source.drift ? CHANT_EXIT.drift : CHANT_EXIT.done;
36
+ return CHANT_EXIT.failed;
37
+ case "apply":
38
+ return code === CHOUDOUFU_APPROVAL_EXIT_CODE && source.refusal === "approval-mismatch" ? CHANT_EXIT.moved : CHANT_EXIT.failed;
39
+ case "live-wave-apply":
40
+ return code === CHOUDOUFU_WAVE_EXIT.setMoved ? CHANT_EXIT.moved : CHANT_EXIT.failed;
41
+ case "live-plan-set":
42
+ return code === 2 ? CHANT_EXIT.done : CHANT_EXIT.failed;
43
+ }
44
+ }
45
+ /** Whether `source` is a plan that moved since its approval: the outcomes the lexicon throws a `planMoved` error for. */
46
+ export function terraformPlanMoved(source) {
47
+ return terraformExitOf(source) === CHANT_EXIT.moved;
48
+ }
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export { EstateRun, estateVersionFallback, estateOutcomes, readEstateApplyText,
8
8
  export { priorStateValues, stateResources, stateOutputs, type StateResourceValues } from "./applied-state.js";
9
9
  export { ComposedRun, composedPlanDigest, changesSomething, type ComposedRunOptions, type ComposedRunStage, type StateRunPlan, type StateRunApply, type StateMemberOutcome, type ComposedStateWrite, type ComposeHook, type ComposeHookMoment, } from "./compose/run.js";
10
10
  export { terraformPlanShape, type TerraformPlanShapeOptions } from "./plan-shape.js";
11
+ export { terraformLockRows, terraformLockRowsFromPart, terraformDefaultLockGrain, terraformLockGrainRefusal } from "./lock-rows.js";
11
12
  export { compositionFor, compositionIfEnabled, composeOff, composeSet, readOrder, moduleNames, fallbackLine, withPlanFailed, identityKey, DEFAULT_COMPOSE_MAX, COMPOSE_STEP_KIND, type Composition, type CompositionInput, type ComposedRead, type ComposeFallback, type ComposeFallbackKind, type ExpectedBinary, } from "./compose.js";
12
13
  export { terraformSerializer } from "./serializer.js";
13
14
  export { terraformConfigSchema, terraformRootSchema, type TerraformConfig, type TerraformRootConfig, type TerraformConfigNamespace, } from "./config.js";
@@ -25,4 +26,5 @@ export { TerraformPinRolloutOp } from "./composites/terraform-pin-rollout-op.js"
25
26
  export type { TerraformPinRolloutOpConfig, TerraformPinRolloutOpResources } from "./composites/terraform-pin-rollout-op.js";
26
27
  export { TERRAFORM_STATE_OWNERSHIP_KEYS } from "./state-ownership.js";
27
28
  export { predictTerraformBehaviour, terraformBehaviourRequest, terraformPredictOptionsFrom, TERRAFORM_REFERENCE_CATALOG, type TerraformBehaviourDeps, type TerraformBehaviourEntity, type TerraformBehaviourRequest, type TerraformBehaviourRequestOptions, type TerraformLiveRead, type TerraformPredictOptions, } from "./behaviour/index.js";
29
+ export { terraformExitOf, terraformPlanMoved, type TerraformExitSource } from "./exit-codes.js";
28
30
  //# sourceMappingURL=index.d.ts.map