@intentius/chant-lexicon-terraform 0.124.51 → 0.124.54

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 (338) hide show
  1. package/README.md +7 -6
  2. package/dist/__fixtures__/pr-loop/estate.component.d.ts +7 -0
  3. package/dist/__fixtures__/pr-loop/estate.component.d.ts.map +1 -0
  4. package/dist/__fixtures__/pr-loop/estate.component.js +7 -0
  5. package/dist/__fixtures__/report/generate.d.ts +26 -0
  6. package/dist/__fixtures__/report/generate.d.ts.map +1 -0
  7. package/dist/__fixtures__/report/generate.js +58 -0
  8. package/dist/applied-state.d.ts +27 -0
  9. package/dist/applied-state.d.ts.map +1 -0
  10. package/dist/applied-state.js +44 -0
  11. package/dist/backend-config.d.ts +53 -0
  12. package/dist/backend-config.d.ts.map +1 -0
  13. package/dist/backend-config.js +136 -0
  14. package/dist/behaviour/kinds.d.ts.map +1 -1
  15. package/dist/behaviour/kinds.js +4 -0
  16. package/dist/behaviour/predict.d.ts.map +1 -1
  17. package/dist/behaviour/predict.js +6 -2
  18. package/dist/behaviour/reference-catalog.d.ts +16 -5
  19. package/dist/behaviour/reference-catalog.d.ts.map +1 -1
  20. package/dist/behaviour/reference-catalog.js +16 -5
  21. package/dist/behaviour/request.d.ts.map +1 -1
  22. package/dist/behaviour/request.js +3 -2
  23. package/dist/changed-roots.d.ts +6 -4
  24. package/dist/changed-roots.d.ts.map +1 -1
  25. package/dist/changed-roots.js +14 -18
  26. package/dist/commands.d.ts +5 -0
  27. package/dist/commands.d.ts.map +1 -1
  28. package/dist/commands.js +87 -4
  29. package/dist/components/capability-plugin.d.ts +65 -3
  30. package/dist/components/capability-plugin.d.ts.map +1 -1
  31. package/dist/components/capability-plugin.js +197 -13
  32. package/dist/components/compose-group.d.ts +145 -0
  33. package/dist/components/compose-group.d.ts.map +1 -0
  34. package/dist/components/compose-group.js +407 -0
  35. package/dist/components/placeholder-preview.d.ts +78 -0
  36. package/dist/components/placeholder-preview.d.ts.map +1 -0
  37. package/dist/components/placeholder-preview.js +254 -0
  38. package/dist/compose/apply-events.d.ts +46 -0
  39. package/dist/compose/apply-events.d.ts.map +1 -0
  40. package/dist/compose/apply-events.js +77 -0
  41. package/dist/compose/estate.d.ts +194 -0
  42. package/dist/compose/estate.d.ts.map +1 -0
  43. package/dist/compose/estate.js +363 -0
  44. package/dist/compose/hcl.d.ts +99 -0
  45. package/dist/compose/hcl.d.ts.map +1 -0
  46. package/dist/compose/hcl.js +302 -0
  47. package/dist/compose/module-address.d.ts +24 -0
  48. package/dist/compose/module-address.d.ts.map +1 -0
  49. package/dist/compose/module-address.js +43 -0
  50. package/dist/compose/root.d.ts +127 -0
  51. package/dist/compose/root.d.ts.map +1 -0
  52. package/dist/compose/root.js +586 -0
  53. package/dist/compose/run.d.ts +183 -0
  54. package/dist/compose/run.d.ts.map +1 -0
  55. package/dist/compose/run.js +447 -0
  56. package/dist/compose/slice.d.ts +102 -0
  57. package/dist/compose/slice.d.ts.map +1 -0
  58. package/dist/compose/slice.js +249 -0
  59. package/dist/compose/state-address.d.ts +28 -0
  60. package/dist/compose/state-address.d.ts.map +1 -0
  61. package/dist/compose/state-address.js +154 -0
  62. package/dist/compose/state-merge.d.ts +52 -0
  63. package/dist/compose/state-merge.d.ts.map +1 -0
  64. package/dist/compose/state-merge.js +135 -0
  65. package/dist/compose.d.ts +179 -0
  66. package/dist/compose.d.ts.map +1 -0
  67. package/dist/compose.js +482 -0
  68. package/dist/composites/terraform-apply-op.d.ts.map +1 -1
  69. package/dist/composites/terraform-apply-op.js +7 -3
  70. package/dist/config.d.ts +59 -13
  71. package/dist/config.d.ts.map +1 -1
  72. package/dist/config.js +43 -9
  73. package/dist/describe-resources.d.ts +74 -12
  74. package/dist/describe-resources.d.ts.map +1 -1
  75. package/dist/describe-resources.js +374 -66
  76. package/dist/drift-read.d.ts +33 -0
  77. package/dist/drift-read.d.ts.map +1 -0
  78. package/dist/drift-read.js +243 -0
  79. package/dist/drift-record.d.ts +147 -0
  80. package/dist/drift-record.d.ts.map +1 -0
  81. package/dist/drift-record.js +383 -0
  82. package/dist/hcl/edges.d.ts.map +1 -1
  83. package/dist/hcl/edges.js +24 -1
  84. package/dist/hcl/files.d.ts +61 -0
  85. package/dist/hcl/files.d.ts.map +1 -0
  86. package/dist/hcl/files.js +221 -0
  87. package/dist/hcl/instances.d.ts +82 -0
  88. package/dist/hcl/instances.d.ts.map +1 -0
  89. package/dist/hcl/instances.js +390 -0
  90. package/dist/hcl/parse.d.ts +81 -9
  91. package/dist/hcl/parse.d.ts.map +1 -1
  92. package/dist/hcl/parse.js +114 -16
  93. package/dist/hcl/roots.d.ts.map +1 -1
  94. package/dist/hcl/roots.js +5 -0
  95. package/dist/hcl/suppressions.d.ts.map +1 -1
  96. package/dist/hcl/suppressions.js +8 -2
  97. package/dist/index.d.ts +11 -2
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +21 -2
  100. package/dist/integrity.json +3 -3
  101. package/dist/live-plan-reasons.d.ts +21 -0
  102. package/dist/live-plan-reasons.d.ts.map +1 -0
  103. package/dist/live-plan-reasons.js +47 -0
  104. package/dist/lsp/option-keys.d.ts.map +1 -1
  105. package/dist/lsp/option-keys.js +2 -0
  106. package/dist/manifest.json +1 -1
  107. package/dist/op/activities/index.d.ts +6 -2
  108. package/dist/op/activities/index.d.ts.map +1 -1
  109. package/dist/op/activities/index.js +5 -1
  110. package/dist/op/activities/live-detect.d.ts +4 -3
  111. package/dist/op/activities/live-detect.d.ts.map +1 -1
  112. package/dist/op/activities/live-detect.js +57 -32
  113. package/dist/op/activities/pin-rollout.d.ts +2 -0
  114. package/dist/op/activities/pin-rollout.d.ts.map +1 -1
  115. package/dist/op/activities/pin-rollout.js +1 -0
  116. package/dist/op/activities/terraform.d.ts +153 -5
  117. package/dist/op/activities/terraform.d.ts.map +1 -1
  118. package/dist/op/activities/terraform.js +205 -20
  119. package/dist/op/activity-contracts.d.ts +27 -0
  120. package/dist/op/activity-contracts.d.ts.map +1 -1
  121. package/dist/op/activity-contracts.js +19 -1
  122. package/dist/op/builders.d.ts +7 -1
  123. package/dist/op/builders.d.ts.map +1 -1
  124. package/dist/op/builders.js +9 -0
  125. package/dist/pin/edit.d.ts.map +1 -1
  126. package/dist/pin/edit.js +35 -0
  127. package/dist/pin/rollout.d.ts +2 -0
  128. package/dist/pin/rollout.d.ts.map +1 -1
  129. package/dist/pin/rollout.js +14 -11
  130. package/dist/plan-shape.d.ts +30 -0
  131. package/dist/plan-shape.d.ts.map +1 -0
  132. package/dist/plan-shape.js +125 -0
  133. package/dist/plugin.d.ts.map +1 -1
  134. package/dist/plugin.js +32 -18
  135. package/dist/report.d.ts +216 -0
  136. package/dist/report.d.ts.map +1 -0
  137. package/dist/report.js +666 -0
  138. package/dist/skills/chant-terraform.md +1 -1
  139. package/dist/state/azure-blob.d.ts +68 -0
  140. package/dist/state/azure-blob.d.ts.map +1 -0
  141. package/dist/state/azure-blob.js +201 -0
  142. package/dist/state/backend.d.ts +158 -0
  143. package/dist/state/backend.d.ts.map +1 -0
  144. package/dist/state/backend.js +406 -0
  145. package/dist/state/exec.d.ts +39 -0
  146. package/dist/state/exec.d.ts.map +1 -0
  147. package/dist/state/exec.js +80 -0
  148. package/dist/state/gcs.d.ts +62 -0
  149. package/dist/state/gcs.d.ts.map +1 -0
  150. package/dist/state/gcs.js +224 -0
  151. package/dist/state/index.d.ts +15 -0
  152. package/dist/state/index.d.ts.map +1 -0
  153. package/dist/state/index.js +14 -0
  154. package/dist/state/locks.d.ts +105 -0
  155. package/dist/state/locks.d.ts.map +1 -0
  156. package/dist/state/locks.js +130 -0
  157. package/dist/state/object-store.d.ts +37 -0
  158. package/dist/state/object-store.d.ts.map +1 -0
  159. package/dist/state/object-store.js +20 -0
  160. package/dist/state/root-states.d.ts +131 -0
  161. package/dist/state/root-states.d.ts.map +1 -0
  162. package/dist/state/root-states.js +241 -0
  163. package/dist/state/s3.d.ts +68 -0
  164. package/dist/state/s3.d.ts.map +1 -0
  165. package/dist/state/s3.js +170 -0
  166. package/dist/state/store.d.ts +51 -0
  167. package/dist/state/store.d.ts.map +1 -0
  168. package/dist/state/store.js +185 -0
  169. package/dist/terragrunt/affected.d.ts.map +1 -1
  170. package/dist/terragrunt/affected.js +2 -1
  171. package/package.json +7 -3
  172. package/src/__fixtures__/ACCEPTANCE.md +69 -7
  173. package/src/__fixtures__/compose/apply-failed-in-app.jsonl +12 -0
  174. package/src/__fixtures__/compose/state-app.json +66 -0
  175. package/src/__fixtures__/compose/state-edge.json +40 -0
  176. package/src/__fixtures__/compose/state-net.json +58 -0
  177. package/src/__fixtures__/compose/three-app.plan.json +184 -0
  178. package/src/__fixtures__/compose/three-composed.plan.json +509 -0
  179. package/src/__fixtures__/compose/three-edge.plan.json +144 -0
  180. package/src/__fixtures__/compose/three-net.plan.json +174 -0
  181. package/src/__fixtures__/compose/unknown-composed.plan.json +228 -0
  182. package/src/__fixtures__/compose/working-after-apply.json +145 -0
  183. package/src/__fixtures__/config-blocks/app/main.tf +61 -0
  184. package/src/__fixtures__/config-blocks/app/modules/network/main.tf +4 -0
  185. package/src/__fixtures__/config-blocks/app-json/main.tf.json +39 -0
  186. package/src/__fixtures__/config-files/shadow/main.tf +3 -0
  187. package/src/__fixtures__/config-files/shadow/main.tofu +3 -0
  188. package/src/__fixtures__/config-files/shadow/other.tf +3 -0
  189. package/src/__fixtures__/config-files/tf/main.tf +11 -0
  190. package/src/__fixtures__/config-files/tf-json/main.tf.json +18 -0
  191. package/src/__fixtures__/config-files/tofu/main.tofu +11 -0
  192. package/src/__fixtures__/config-files/tofu-json/main.tofu.json +18 -0
  193. package/src/__fixtures__/config-files/tofu-only/main.tofu +3 -0
  194. package/src/__fixtures__/drift-record/README.md +11 -0
  195. package/src/__fixtures__/drift-record/live-plan-sections.json +27 -0
  196. package/src/__fixtures__/instance-forms/app/main.tf +110 -0
  197. package/src/__fixtures__/instance-forms/app/modules/svc/main.tf +3 -0
  198. package/src/__fixtures__/instance-vars/app/main.tf +52 -0
  199. package/src/__fixtures__/instance-vars/app/modules/app/main.tf +12 -0
  200. package/src/__fixtures__/instance-vars/app/modules/edge/main.tf +3 -0
  201. package/src/__fixtures__/instance-vars/app/terraform.tfvars +1 -0
  202. package/src/__fixtures__/live-module-keyed/live-graph.json +259 -0
  203. package/src/__fixtures__/live-module-keyed/live-plan.json +87 -0
  204. package/src/__fixtures__/live-module-keyed/root/estate.chdf.hcl +1 -0
  205. package/src/__fixtures__/live-module-keyed/root/main.tf +21 -0
  206. package/src/__fixtures__/live-module-keyed/root/modules/app/main.tf +17 -0
  207. package/src/__fixtures__/live-module-keyed/root/modules/edge/main.tf +7 -0
  208. package/src/__fixtures__/live-module-keyed/root/versions.tf +30 -0
  209. package/src/__fixtures__/live-module-keyed/show-state.json +146 -0
  210. package/src/__fixtures__/pr-loop/README.md +50 -0
  211. package/src/__fixtures__/pr-loop/chant.config.json +13 -0
  212. package/src/__fixtures__/pr-loop/estate.component.ts +10 -0
  213. package/src/__fixtures__/pr-loop/roots/a/main.tf +11 -0
  214. package/src/__fixtures__/pr-loop/roots/app/main.tf +19 -0
  215. package/src/__fixtures__/pr-loop/roots/b/main.tf +7 -0
  216. package/src/__fixtures__/pr-loop/roots/dns/main.tf +3 -0
  217. package/src/__fixtures__/pr-loop/roots/net/main.tf +7 -0
  218. package/src/__fixtures__/report/applied.show.json +51 -0
  219. package/src/__fixtures__/report/generate.ts +63 -0
  220. package/src/__fixtures__/report/plan.show.json +200 -0
  221. package/src/__fixtures__/report/tf-apply/change-set.json +170 -0
  222. package/src/__fixtures__/report/tf-apply/report.json +338 -0
  223. package/src/__fixtures__/report/tf-plan/change-set.json +170 -0
  224. package/src/__fixtures__/report/tf-plan/report.json +320 -0
  225. package/src/__fixtures__/three-cloud-azure.graph.json +290 -0
  226. package/src/applied-state.ts +52 -0
  227. package/src/backend-config.test.ts +105 -0
  228. package/src/backend-config.ts +133 -0
  229. package/src/behaviour/kinds.ts +4 -0
  230. package/src/behaviour/predict.ts +5 -2
  231. package/src/behaviour/reference-catalog.plugin.test.ts +65 -0
  232. package/src/behaviour/reference-catalog.ts +16 -5
  233. package/src/behaviour/request.ts +3 -2
  234. package/src/change-set-instances.test.ts +98 -0
  235. package/src/changed-roots.ts +14 -16
  236. package/src/commands.ts +84 -4
  237. package/src/components/capability-plugin.test.ts +172 -5
  238. package/src/components/capability-plugin.ts +212 -11
  239. package/src/components/change-set-graph.e2e.test.ts +11 -5
  240. package/src/components/compose-group.test.ts +486 -0
  241. package/src/components/compose-group.ts +489 -0
  242. package/src/components/demo-composed-roots.e2e.test.ts +440 -0
  243. package/src/components/member-outputs.test.ts +76 -0
  244. package/src/components/placeholder-preview.test.ts +147 -0
  245. package/src/components/placeholder-preview.ts +257 -0
  246. package/src/components/pr-loop-compose.tofu.e2e.test.ts +363 -0
  247. package/src/components/pr-loop.tofu.test.ts +112 -56
  248. package/src/compose/apply-events.test.ts +62 -0
  249. package/src/compose/apply-events.ts +100 -0
  250. package/src/compose/estate.choudoufu.e2e.test.ts +112 -0
  251. package/src/compose/estate.test.ts +421 -0
  252. package/src/compose/estate.ts +443 -0
  253. package/src/compose/hcl.ts +311 -0
  254. package/src/compose/module-address.ts +49 -0
  255. package/src/compose/root.test.ts +416 -0
  256. package/src/compose/root.ts +592 -0
  257. package/src/compose/run.test.ts +440 -0
  258. package/src/compose/run.tofu.test.ts +176 -0
  259. package/src/compose/run.ts +527 -0
  260. package/src/compose/slice.test.ts +184 -0
  261. package/src/compose/slice.ts +290 -0
  262. package/src/compose/state-address.ts +157 -0
  263. package/src/compose/state-merge.test.ts +131 -0
  264. package/src/compose/state-merge.ts +148 -0
  265. package/src/compose.test.ts +248 -0
  266. package/src/compose.ts +627 -0
  267. package/src/composites/terraform-apply-op.test.ts +9 -0
  268. package/src/composites/terraform-apply-op.ts +7 -3
  269. package/src/composites/terraform-pin-rollout-op.test.ts +2 -2
  270. package/src/config-file-listers.test.ts +265 -0
  271. package/src/config.ts +44 -9
  272. package/src/describe-resources.engine-facts.test.ts +412 -0
  273. package/src/describe-resources.live-module-keyed.test.ts +290 -0
  274. package/src/describe-resources.ts +416 -70
  275. package/src/drift-read.ts +284 -0
  276. package/src/drift-record.test.ts +223 -0
  277. package/src/drift-record.ts +455 -0
  278. package/src/hcl/config-blocks.test.ts +174 -0
  279. package/src/hcl/edges.ts +21 -1
  280. package/src/hcl/files.ts +234 -0
  281. package/src/hcl/instance-forms.test.ts +94 -0
  282. package/src/hcl/instances.test.ts +137 -0
  283. package/src/hcl/instances.ts +422 -0
  284. package/src/hcl/parse.test.ts +139 -1
  285. package/src/hcl/parse.ts +149 -16
  286. package/src/hcl/roots.ts +6 -0
  287. package/src/hcl/suppressions.ts +8 -2
  288. package/src/index.ts +76 -0
  289. package/src/live-plan-reasons.ts +54 -0
  290. package/src/lsp/completions.test.ts +1 -1
  291. package/src/lsp/option-keys.ts +2 -0
  292. package/src/op/activities/choudoufu-plan-set-op.test.ts +111 -0
  293. package/src/op/activities/index.ts +9 -0
  294. package/src/op/activities/live-detect.ts +55 -26
  295. package/src/op/activities/pin-rollout.ts +3 -0
  296. package/src/op/activities/registry.test.ts +2 -1
  297. package/src/op/activities/terraform.test.ts +55 -0
  298. package/src/op/activities/terraform.ts +266 -22
  299. package/src/op/activity-contracts.test.ts +1 -0
  300. package/src/op/activity-contracts.ts +24 -1
  301. package/src/op/builders.ts +14 -0
  302. package/src/pin/bundle.test.ts +2 -0
  303. package/src/pin/edit.ts +30 -0
  304. package/src/pin/rollout.ts +16 -11
  305. package/src/plan-shape.test.ts +148 -0
  306. package/src/plan-shape.ts +128 -0
  307. package/src/plugin.ts +33 -19
  308. package/src/report.test.ts +423 -0
  309. package/src/report.ts +752 -0
  310. package/src/signal-only-root.test.ts +72 -0
  311. package/src/skills/chant-terraform.md +1 -1
  312. package/src/state/azure-blob.ts +223 -0
  313. package/src/state/backend-verdict.test.ts +187 -0
  314. package/src/state/backend.test.ts +127 -0
  315. package/src/state/backend.ts +488 -0
  316. package/src/state/exec.ts +111 -0
  317. package/src/state/gcs.ts +254 -0
  318. package/src/state/index.ts +15 -0
  319. package/src/state/locks.test.ts +84 -0
  320. package/src/state/locks.ts +201 -0
  321. package/src/state/object-store.ts +35 -0
  322. package/src/state/root-states-floci.e2e.test.ts +66 -0
  323. package/src/state/root-states.test.ts +462 -0
  324. package/src/state/root-states.ts +309 -0
  325. package/src/state/s3.ts +193 -0
  326. package/src/state/store.ts +225 -0
  327. package/src/terragrunt/affected.ts +2 -1
  328. package/src/workspace-kinds.test.ts +4 -17
  329. package/workspace-kinds.json +1 -24
  330. package/dist/workspace-member.d.ts +0 -30
  331. package/dist/workspace-member.d.ts.map +0 -1
  332. package/dist/workspace-member.js +0 -91
  333. package/schemas/pulumi.schema.json +0 -34
  334. package/src/testdata/pulumi-member/Pulumi.yaml +0 -2
  335. package/src/testdata/pulumi-member/stack-export.json +0 -53
  336. package/src/testdata/pulumi-unexported/Pulumi.yaml +0 -2
  337. package/src/workspace-member.test.ts +0 -89
  338. package/src/workspace-member.ts +0 -126
@@ -44,10 +44,12 @@ import { tmpdir } from "node:os";
44
44
  import { promisify } from "node:util";
45
45
  import { resolve, dirname, join, relative, sep } from "node:path";
46
46
  import { loadChantConfigUpward } from "@intentius/chant/config";
47
+ import { findWorkspaceRoot } from "@intentius/chant/project-root";
47
48
  import { liveReadKey, memoLiveRead } from "@intentius/chant/live-read-session";
48
49
  import { terraformPlanDigest } from "../../plan-digest.js";
49
50
  import { choudoufuSetPlanParts, plannerForBinary, terraformChangeSetPart } from "../../change-set.js";
50
51
  import { detectLiveEstate } from "./live-detect.js";
52
+ import { backendConfigValues } from "../../backend-config.js";
51
53
  import { parseAdoptionCommands, readAdoptionLedger, renderAdoptionLedger, } from "../adoption.js";
52
54
  const execAsync = promisify(exec);
53
55
  /**
@@ -100,6 +102,34 @@ export const MIN_CHOUDOUFU_VERSION = "0.15.0";
100
102
  * choudoufu#1754, and {@link choudoufuPlanSet} refuses it.
101
103
  */
102
104
  export const MIN_CHOUDOUFU_WAVES_VERSION = "0.22.0";
105
+ /**
106
+ * The choudoufu release a composed run of estates needs (#4445): a root with
107
+ * no `live` block that calls estate directories as modules, planned and
108
+ * applied as one walk, with a `terraform_estate_outputs` read of another
109
+ * estate in the run answered inside it (choudoufu#2059, first released in
110
+ * v0.31.0). Only the composed estate run (`compose/estate.ts`) checks it;
111
+ * below it the members plan wave by wave. A dev build names no release and
112
+ * passes, as for the other floors.
113
+ */
114
+ export const MIN_CHOUDOUFU_ESTATES_VERSION = "0.31.0";
115
+ /**
116
+ * The choudoufu release whose `live-graph -json -from-plan=<file>` prints the
117
+ * engine graph document (chant#4520's design section 4, choudoufu#2285), the
118
+ * one `describeResources()` reads engine facts from on a live root
119
+ * (chant#4528). choudoufu's changelog lists the command under
120
+ * `v0.32.0 (Unreleased)`, so this is the version it releases as. The chant
121
+ * side of the same contract, the first chant that writes those facts, is
122
+ * `ENGINE_FACTS_FLOOR` in core's engine-facts.ts, which is NEXT_RELEASE
123
+ * until the cut.
124
+ *
125
+ * Not a refusal. Facts are for people and never feed a decision, so a
126
+ * choudoufu without the command (a usage error, see
127
+ * {@link classifyLiveGraphFailure}) leaves `facts` absent with one note
128
+ * naming this version, and the rest of the read is unchanged. The check is
129
+ * the capability, not the version string: a local or dev build names a
130
+ * version below this or none at all, and still carries the command.
131
+ */
132
+ export const MIN_CHOUDOUFU_GRAPH_VERSION = "0.32.0";
103
133
  /**
104
134
  * Where a choudoufu root's wave files live, relative to the project root:
105
135
  * `<root>/plan-set.json` (the set plan document chant planned and the
@@ -151,16 +181,21 @@ export function terraformEnvironment(root) {
151
181
  ...(root?.workspace ? { TF_WORKSPACE: root.workspace } : {}),
152
182
  };
153
183
  }
154
- /** `terraform init`, with the root's `backendConfig` as `-backend-config=k=v` flags. */
184
+ /**
185
+ * `terraform init`, with the root's `backendConfigFiles` as
186
+ * `-backend-config=<path>` flags and then its `backendConfig` as
187
+ * `-backend-config=k=v` flags (chant#4459): init applies later flags over
188
+ * earlier ones, so a pair overrides a file. A path is passed as written; init
189
+ * runs in the root's directory, which a relative one resolves against.
190
+ */
155
191
  export function terraformInitCommand(opts) {
156
192
  const parts = [opts.binary, "init", "-input=false"];
157
193
  if (opts.upgrade)
158
194
  parts.push("-upgrade");
159
195
  if (opts.reconfigure)
160
196
  parts.push("-reconfigure");
161
- for (const [key, value] of Object.entries(opts.backendConfig ?? {})) {
162
- parts.push(`-backend-config=${quoteArg(`${key}=${value}`)}`);
163
- }
197
+ for (const value of backendConfigValues(opts))
198
+ parts.push(`-backend-config=${quoteArg(value)}`);
164
199
  return parts.join(" ");
165
200
  }
166
201
  /**
@@ -229,6 +264,39 @@ export function choudoufuLivePlanCommand(opts) {
229
264
  parts.push(`-estate=${quoteArg(opts.estate)}`);
230
265
  return parts.join(" ");
231
266
  }
267
+ /**
268
+ * `live-graph -json -from-plan=<file> [-estate=<estate>]` (choudoufu#2285).
269
+ * No `DIR` argument: like {@link choudoufuLiveCheckCommand}, the activity
270
+ * runs in the root directory, which is DIR's default. `-estate` follows
271
+ * {@link choudoufuLivePlanCommand}'s rule: only for a root that does not
272
+ * name its own estate. `-history` is never passed: record versions take a
273
+ * listing per record and `s3:ListBucketVersions`, and an observation read
274
+ * has no use for them (the #4520 ruling, Q6: opt-in).
275
+ */
276
+ export function choudoufuLiveGraphCommand(opts) {
277
+ const parts = [opts.binary, "live-graph", "-json", `-from-plan=${quoteArg(opts.fromPlan)}`];
278
+ if (opts.estate !== undefined)
279
+ parts.push(`-estate=${quoteArg(opts.estate)}`);
280
+ return parts.join(" ");
281
+ }
282
+ /**
283
+ * Whether a failed `live-graph` run is a choudoufu that has no such command
284
+ * or flag (`"unsupported"`), or a real failure (`"failed"`). Older builds
285
+ * answer an unknown command two ways: the new CLI prints `OpenTofu has no
286
+ * command named "live-graph"` and exits 1 (cmd/choudoufu/command_main.go),
287
+ * and the older mitchellh/cli path prints its usage and exits 127. A build
288
+ * whose `live-graph` predates `-from-plan` fails the flag parse with `flag
289
+ * provided but not defined`.
290
+ */
291
+ export function classifyLiveGraphFailure(code, output) {
292
+ if (code === 127)
293
+ return "unsupported";
294
+ if (/no command named "?live-graph"?/i.test(output))
295
+ return "unsupported";
296
+ if (/flag provided but not defined: -(?:from-plan|json|estate)\b/.test(output))
297
+ return "unsupported";
298
+ return "failed";
299
+ }
232
300
  /** `live-ls -estate=<estate> -json [-consistent]`. */
233
301
  export function choudoufuLiveLsCommand(opts) {
234
302
  const parts = [opts.binary, "live-ls", `-estate=${quoteArg(opts.estate)}`, "-json"];
@@ -477,8 +545,8 @@ function report(stdout, stderr) {
477
545
  }
478
546
  // ── Activities ──────────────────────────────────────────────────────────────
479
547
  /**
480
- * `terraform init` in the named root, with the root's `backendConfig` supplied
481
- * as `-backend-config` flags. Uses the longInfra profile: init downloads
548
+ * `terraform init` in the named root, with the root's `backendConfigFiles` and
549
+ * `backendConfig` supplied as `-backend-config` flags. Uses the longInfra profile: init downloads
482
550
  * providers and modules, a network call of unpredictable size.
483
551
  */
484
552
  export async function terraformInit(args, signal) {
@@ -486,16 +554,53 @@ export async function terraformInit(args, signal) {
486
554
  const cmd = terraformInitCommand({
487
555
  binary,
488
556
  ...(root.backendConfig ? { backendConfig: root.backendConfig } : {}),
557
+ ...(root.backendConfigFiles ? { backendConfigFiles: root.backendConfigFiles } : {}),
489
558
  ...(args.upgrade ? { upgrade: true } : {}),
490
559
  ...(args.reconfigure ? { reconfigure: true } : {}),
491
560
  });
492
- const { stdout, stderr } = await run(cmd, dir, terraformEnvironment(root), signal);
493
- report(stdout, stderr);
494
- return { dir, ...(root.workspace ? { workspace: root.workspace } : {}) };
561
+ // Known once the root resolves, before init runs (chant#4512), so a failed
562
+ // init still publishes them and the ledger names the root it was for.
563
+ const rootPath = workspaceRelativePath(dir);
564
+ const result = { dir, ...(root.workspace ? { workspace: root.workspace } : {}), ...(rootPath !== undefined ? { rootPath } : {}) };
565
+ let out;
566
+ try {
567
+ out = await run(cmd, dir, terraformEnvironment(root), signal);
568
+ }
569
+ catch (err) {
570
+ throw withKnownOutputs(err, result);
571
+ }
572
+ report(out.stdout, out.stderr);
573
+ return result;
574
+ }
575
+ /**
576
+ * `err` carrying `outputs` as the outputs the activity knew when it failed
577
+ * (chant#4512): the `knownOutputs` property core's executor reads a failed
578
+ * step's outcome attributes from (core's `withKnownOutputs`). It is set here
579
+ * rather than imported so the lexicon needs no newer core; an older executor
580
+ * reads the error as any other failure.
581
+ */
582
+ function withKnownOutputs(err, outputs) {
583
+ const e = err instanceof Error ? err : new Error(String(err));
584
+ return Object.assign(e, { knownOutputs: outputs });
585
+ }
586
+ /**
587
+ * `dir` relative to the chant workspace that holds it, `/`-separated, or
588
+ * undefined outside a workspace or outside its root (chant#4482). The
589
+ * workspace root `"."` stands for itself.
590
+ */
591
+ export function workspaceRelativePath(dir) {
592
+ const found = findWorkspaceRoot(dir);
593
+ if (!found)
594
+ return undefined;
595
+ const rel = relative(found.dir, dir);
596
+ if (rel.startsWith(".."))
597
+ return undefined;
598
+ return rel === "" ? "." : rel.split(sep).join("/");
495
599
  }
496
600
  /**
497
601
  * `terraform plan -detailed-exitcode -out=<planFile>` in the named root, then
498
- * `terraform show` over the written plan in both `-json` and `-no-color` form.
602
+ * `terraform show` over the written plan in both `-json` and `-no-color` form
603
+ * (`-json` alone with `text: false`).
499
604
  *
500
605
  * The exit code is the answer, not an error condition: 0 is a plan with no
501
606
  * changes, 2 is a plan with changes, and anything else, 1 included, is a
@@ -538,14 +643,14 @@ export async function terraformPlan(args, signal) {
538
643
  changed = true;
539
644
  }
540
645
  const jsonRun = await run(terraformShowCommand({ binary, json: true, planFile }), dir, env, signal);
541
- const textRun = await run(terraformShowCommand({ binary, json: false, planFile }), dir, env, signal);
646
+ const text = args.text === false ? "" : (await run(terraformShowCommand({ binary, json: false, planFile }), dir, env, signal)).stdout;
542
647
  const json = JSON.parse(jsonRun.stdout);
543
648
  return {
544
649
  changed,
545
650
  planFile,
546
651
  dir,
547
652
  json,
548
- text: textRun.stdout,
653
+ text,
549
654
  planDigest: terraformPlanDigest(json),
550
655
  changeSet: terraformChangeSetPart({
551
656
  member: args.root,
@@ -629,7 +734,10 @@ export async function terraformApply(args, signal) {
629
734
  * asked for the values to hand them on.
630
735
  */
631
736
  export async function terraformOutputs(args, signal) {
632
- const { binary, root, dir } = await resolveRoot(args, signal);
737
+ return outputsIn(await resolveRoot(args, signal), signal);
738
+ }
739
+ /** `<binary> output -json` in a resolved root, as `{ name: value }`. */
740
+ async function outputsIn({ binary, root, dir }, signal) {
633
741
  const { stdout } = await run(`${binary} output -json`, dir, terraformEnvironment(root), signal);
634
742
  const parsed = JSON.parse(stdout || "{}");
635
743
  return Object.fromEntries(Object.entries(parsed).map(([name, entry]) => [name, entry?.value]));
@@ -694,7 +802,7 @@ export async function choudoufuWaveRoot(args, signal) {
694
802
  if (resolved.root.varFiles && resolved.root.varFiles.length > 0) {
695
803
  return { wave: false, reason: "the root has var files, which live-plan-set does not take" };
696
804
  }
697
- if (resolved.root.backendConfig && Object.keys(resolved.root.backendConfig).length > 0) {
805
+ if ((resolved.root.backendConfig && Object.keys(resolved.root.backendConfig).length > 0) || (resolved.root.backendConfigFiles?.length ?? 0) > 0) {
698
806
  return { wave: false, reason: "the root has backend config, which live-plan-set does not take" };
699
807
  }
700
808
  const root = choudoufuSetRootPath(resolved.projectRoot, resolved.dir);
@@ -764,15 +872,18 @@ function fromProject(projectRoot, path) {
764
872
  * `live-wave-apply` hands back to choudoufu with the set digest.
765
873
  */
766
874
  export async function choudoufuPlanSet(args, signal) {
767
- const resolved = await resolveRoot(args, signal);
875
+ return planSetIn(await resolveRoot(args, signal), args.root, signal);
876
+ }
877
+ /** {@link choudoufuPlanSet} over a resolved root, `key` naming it in errors, in the waves directory and as the change-set member. */
878
+ async function planSetIn(resolved, key, signal) {
768
879
  const { binary, root, dir, projectRoot } = resolved;
769
880
  if (binary !== "choudoufu")
770
- throw new Error(`choudoufuPlanSet: root "${args.root}" runs ${binary}, not choudoufu`);
881
+ throw new Error(`choudoufuPlanSet: root "${key}" runs ${binary}, not choudoufu`);
771
882
  await ensureChoudoufuWavesVersion(binary, signal);
772
883
  const rootPath = choudoufuSetRootPath(projectRoot, dir);
773
884
  if (rootPath === undefined)
774
- throw new Error(`choudoufuPlanSet: root "${args.root}" (${dir}) is not inside ${projectRoot}`);
775
- const wavesDir = choudoufuWavesDir(projectRoot, args.root);
885
+ throw new Error(`choudoufuPlanSet: root "${key}" (${dir}) is not inside ${projectRoot}`);
886
+ const wavesDir = choudoufuWavesDir(projectRoot, key);
776
887
  mkdirSync(wavesDir, { recursive: true });
777
888
  const cmd = choudoufuPlanSetCommand({ binary, root: rootPath, outDir: fromProject(projectRoot, join(wavesDir, "out")) });
778
889
  let stdout;
@@ -790,11 +901,42 @@ export async function choudoufuPlanSet(args, signal) {
790
901
  report("", failure.stderr ?? "");
791
902
  stdout = failure.stdout ?? "";
792
903
  }
793
- const parsed = parseChoudoufuPlanSet(stdout, { member: args.root, root: rootPath });
904
+ const parsed = parseChoudoufuPlanSet(stdout, { member: key, root: rootPath });
794
905
  const planSetFile = join(wavesDir, "plan-set.json");
795
906
  writeFileSync(planSetFile, stdout);
796
907
  return { ...parsed, planSetFile, root: rootPath };
797
908
  }
909
+ async function resolveMemberRoot(args, signal) {
910
+ const binary = "choudoufu";
911
+ await ensureChoudoufuVersion(binary, signal);
912
+ const dir = resolve(args.dir);
913
+ const estate = detectLiveEstate(dir);
914
+ return { binary, root: { dir }, dir, ...(estate !== undefined ? { estate } : {}), live: estate !== undefined, projectRoot: resolve(args.projectRoot) };
915
+ }
916
+ /**
917
+ * The member's outputs as applied (#4455): `choudoufu init`, then `choudoufu
918
+ * output -json` in its directory, the read the `terraform-apply` capability's
919
+ * `outputs` makes for a root. A live estate's outputs come from its record
920
+ * store.
921
+ */
922
+ export async function choudoufuMemberOutputs(args, signal) {
923
+ const resolved = await resolveMemberRoot(args, signal);
924
+ const { stdout, stderr } = await run(terraformInitCommand({ binary: resolved.binary }), resolved.dir, terraformEnvironment(resolved.root), signal);
925
+ report(stdout, stderr);
926
+ return outputsIn(resolved, signal);
927
+ }
928
+ /**
929
+ * The member planned as a one-root set (#4455), the way a choudoufu root in
930
+ * a gated wave is ({@link choudoufuPlanSet}): `live-plan-set -json` from
931
+ * `projectRoot`. The document's embedded plan carries `planned_values` and
932
+ * `output_changes`, which say what the member's outputs will be.
933
+ */
934
+ export async function choudoufuMemberPlanSet(args, signal) {
935
+ const resolved = await resolveMemberRoot(args, signal);
936
+ if (!resolved.live)
937
+ throw new Error(`choudoufu member "${args.member}" (${resolved.dir}) declares no live estate, so live-plan-set cannot plan it`);
938
+ return planSetIn(resolved, args.member, signal);
939
+ }
798
940
  /**
799
941
  * The resume file to hand choudoufu for `setDigest`, from what an earlier
800
942
  * attempt carried, or `undefined`. choudoufu refuses a resume file written
@@ -958,6 +1100,7 @@ export function terraformInitLine(opts) {
958
1100
  const init = terraformInitCommand({
959
1101
  binary: opts.binary,
960
1102
  ...(opts.backendConfig ? { backendConfig: opts.backendConfig } : {}),
1103
+ ...(opts.backendConfigFiles ? { backendConfigFiles: opts.backendConfigFiles } : {}),
961
1104
  });
962
1105
  return init.replace(`${opts.binary} init`, `${opts.binary} -chdir=${quoteArg(opts.dir)} init`);
963
1106
  }
@@ -1019,7 +1162,12 @@ export function rootIsInitialised(dir, env = process.env) {
1019
1162
  export async function terraformPlanRead(args, signal) {
1020
1163
  const { binary, root, dir } = await resolveRoot(args, signal);
1021
1164
  const env = terraformEnvironment(root);
1022
- const initLine = terraformInitLine({ binary, dir, ...(root.backendConfig ? { backendConfig: root.backendConfig } : {}) });
1165
+ const initLine = terraformInitLine({
1166
+ binary,
1167
+ dir,
1168
+ ...(root.backendConfig ? { backendConfig: root.backendConfig } : {}),
1169
+ ...(root.backendConfigFiles ? { backendConfigFiles: root.backendConfigFiles } : {}),
1170
+ });
1023
1171
  const notInitialised = (detail) => new TerraformPlanReadError("not-initialised", `terraform.roots.${args.root} (${dir}) ${detail}: run \`${initLine}\` first. chant never runs init on a read, since init writes into the root.`, dir, initLine);
1024
1172
  if (!rootIsInitialised(dir, { ...process.env, ...env }))
1025
1173
  throw notInitialised("has never been initialised");
@@ -1255,6 +1403,43 @@ export async function choudoufuLivePlan(args, signal) {
1255
1403
  ...parseChoudoufuPlanSummary(text),
1256
1404
  };
1257
1405
  }
1406
+ /**
1407
+ * `choudoufu live-graph -json -from-plan=<file>` (choudoufu#2285, read by
1408
+ * chant#4528): the root's graph as one engine graph document, its facts taken
1409
+ * from a `live-plan -json` document this process already read. No second live
1410
+ * read: choudoufu builds the graph from the configuration and that file
1411
+ * alone, starts no provider and calls no cloud, so facts and verdicts come
1412
+ * from the same refresh (choudoufu #1014).
1413
+ *
1414
+ * A usage error ({@link classifyLiveGraphFailure}) resolves `supported:
1415
+ * false` rather than throwing, since the caller degrades to no facts; any
1416
+ * other failure throws with choudoufu's output. Warnings (an empty scope
1417
+ * estate, a plan without `observed_at`) go to stderr and are echoed like
1418
+ * every other activity's.
1419
+ */
1420
+ export async function choudoufuLiveGraph(args, signal) {
1421
+ const resolved = await resolveRoot(args, signal);
1422
+ const { binary, dir } = resolved;
1423
+ const env = terraformEnvironment(resolved.root);
1424
+ const estateFlag = resolved.estate === undefined && args.estate !== undefined ? { estate: args.estate } : {};
1425
+ const cmd = choudoufuLiveGraphCommand({ binary, fromPlan: args.fromPlan, ...estateFlag });
1426
+ try {
1427
+ const { stdout, stderr } = await run(cmd, dir, env, signal);
1428
+ if (stderr)
1429
+ console.error(stderr);
1430
+ return { supported: true, json: JSON.parse(liveDocumentFrom(stdout)), dir };
1431
+ }
1432
+ catch (err) {
1433
+ const failure = err;
1434
+ if (typeof failure.code !== "number")
1435
+ throw err;
1436
+ const output = `${failure.stderr ?? ""}\n${failure.stdout ?? ""}`;
1437
+ const detail = output.trim().split("\n").find((line) => line.trim() !== "")?.trim() ?? `exit ${failure.code}`;
1438
+ if (classifyLiveGraphFailure(failure.code, output) === "unsupported")
1439
+ return { supported: false, detail, dir };
1440
+ throw new Error(`${binary} live-graph failed in ${dir} (exit ${failure.code})${detail ? `\n${output.trim()}` : ""}`);
1441
+ }
1442
+ }
1258
1443
  /**
1259
1444
  * `choudoufu live-ls -estate=<estate> -json [-consistent]`: every resource
1260
1445
  * the account holds under the estate, read straight from the Resource Groups
@@ -37,6 +37,7 @@ export declare const terraformInitContract: import("@intentius/chant/op").Activi
37
37
  }, {
38
38
  dir: string;
39
39
  workspace?: string | undefined;
40
+ rootPath?: string | undefined;
40
41
  }>;
41
42
  export declare const terraformPlanContract: import("@intentius/chant/op").ActivityContract<{
42
43
  root: string;
@@ -155,6 +156,32 @@ export declare const choudoufuAdoptContract: import("@intentius/chant/op").Activ
155
156
  ambiguous: number;
156
157
  dir: string;
157
158
  }>;
159
+ /**
160
+ * `choudoufuPlanSet` (#4483): one live choudoufu root planned as a one-root
161
+ * set. `changeSet` is the root's part, the field an Op hands to
162
+ * `composeChangeSet`; `planDigest` is what a gate binds; `planSetFile` and
163
+ * `setDigest` are what a later `live-wave-apply` takes.
164
+ */
165
+ export declare const choudoufuPlanSetContract: import("@intentius/chant/op").ActivityContract<{
166
+ root: string;
167
+ cwd?: string | undefined;
168
+ }, {
169
+ setDigest: string;
170
+ rootDigest: string;
171
+ planDigest: string;
172
+ changed: boolean;
173
+ json: unknown;
174
+ changeSet: {
175
+ member: {
176
+ [x: string]: unknown;
177
+ member: string;
178
+ planDigest: string | null;
179
+ };
180
+ entries: unknown[];
181
+ };
182
+ planSetFile: string;
183
+ root: string;
184
+ }>;
158
185
  /**
159
186
  * `terraformPinRollout` (#3189). It names no `terraform.roots` key: a rollout
160
187
  * moves a module's pin across many roots, found by directory, so it carries no
@@ -1 +1 @@
1
- {"version":3,"file":"activity-contracts.d.ts","sourceRoot":"","sources":["../../src/op/activity-contracts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AA+BH,eAAO,MAAM,qBAAqB;;;;;;;;EASjC,CAAC;AAEF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;EAoBjC,CAAC;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;EAWlC,CAAC;AAEF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;EAYjC,CAAC;AAEF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;EAwBrC,CAAC;AAEF,eAAO,MAAM,uBAAuB;;;;;;;;;EASnC,CAAC;AAEF,eAAO,MAAM,0BAA0B;;;;;;;;EAKtC,CAAC;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;EAgBlC,CAAC;AAKF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BvC,CAAC"}
1
+ {"version":3,"file":"activity-contracts.d.ts","sourceRoot":"","sources":["../../src/op/activity-contracts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AA+BH,eAAO,MAAM,qBAAqB;;;;;;;;;EAUjC,CAAC;AAEF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;EAoBjC,CAAC;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;EAWlC,CAAC;AAEF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;EAYjC,CAAC;AAEF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;EAwBrC,CAAC;AAEF,eAAO,MAAM,uBAAuB;;;;;;;;;EASnC,CAAC;AAEF,eAAO,MAAM,0BAA0B;;;;;;;;EAKtC,CAAC;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;EAgBlC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;EAcpC,CAAC;AAKF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BvC,CAAC"}
@@ -57,7 +57,9 @@ export const terraformInitContract = activityContract("terraformInit", z.strictO
57
57
  ...rootArgs,
58
58
  upgrade: z.boolean().optional(),
59
59
  reconfigure: z.boolean().optional(),
60
- }), z.object({ dir: z.string(), workspace: z.string().optional() }), rootEntity);
60
+ }),
61
+ // `rootPath` (#4482) is what TerraformApplyOp's Init step publishes as `TerraformRootPath`; OPS012 checks it here.
62
+ z.object({ dir: z.string(), workspace: z.string().optional(), rootPath: z.string().optional() }), rootEntity);
61
63
  export const terraformPlanContract = activityContract("terraformPlan", z.strictObject({
62
64
  ...rootArgs,
63
65
  planFile: z.string().optional(),
@@ -127,6 +129,22 @@ export const choudoufuAdoptContract = activityContract("choudoufuAdopt", z.stric
127
129
  ambiguous: z.number(),
128
130
  dir: z.string(),
129
131
  }), rootEntity);
132
+ /**
133
+ * `choudoufuPlanSet` (#4483): one live choudoufu root planned as a one-root
134
+ * set. `changeSet` is the root's part, the field an Op hands to
135
+ * `composeChangeSet`; `planDigest` is what a gate binds; `planSetFile` and
136
+ * `setDigest` are what a later `live-wave-apply` takes.
137
+ */
138
+ export const choudoufuPlanSetContract = activityContract("choudoufuPlanSet", z.strictObject({ ...rootArgs }), z.object({
139
+ setDigest: z.string(),
140
+ rootDigest: z.string(),
141
+ planDigest: z.string(),
142
+ changed: z.boolean(),
143
+ json: z.unknown(),
144
+ changeSet: z.object({ member: z.object({ member: z.string(), planDigest: z.string().nullable() }).passthrough(), entries: z.array(z.unknown()) }),
145
+ planSetFile: z.string(),
146
+ root: z.string(),
147
+ }), rootEntity);
130
148
  /** One root of a pin-bump rollout ({@link PinRoot} in `../pin/waves.ts`). */
131
149
  const pinRoot = z.strictObject({ root: z.string(), dependsOn: z.array(z.string()).optional(), tsSource: z.string().optional() });
132
150
  /**
@@ -15,7 +15,7 @@
15
15
  * path a second time.
16
16
  */
17
17
  import { type ActivityStep, type NamedActivityStep, type WithStepRefs } from "@intentius/chant/op";
18
- import type { TerraformInitArgs, TerraformPlanArgs, TerraformApplyArgs, TerraformShowArgs, ChoudoufuLivePlanArgs, ChoudoufuLiveLsArgs, ChoudoufuLiveCheckArgs, ChoudoufuAdoptArgs } from "./activities/terraform.js";
18
+ import type { TerraformInitArgs, TerraformPlanArgs, TerraformApplyArgs, TerraformShowArgs, ChoudoufuLivePlanArgs, ChoudoufuLiveLsArgs, ChoudoufuLiveCheckArgs, ChoudoufuAdoptArgs, TerraformRootArgs } from "./activities/terraform.js";
19
19
  import type { TerraformPinRolloutArgs } from "./activities/pin-rollout.js";
20
20
  /** Extra opts every wrapper below accepts alongside its activity's own fields. */
21
21
  type StepOpts = {
@@ -95,6 +95,12 @@ export declare const choudoufuAdopt: (root: string, opts: WithStepRefs<Omit<Chou
95
95
  * provider.
96
96
  */
97
97
  export declare const terraformShow: (root: string, opts?: WithStepRefs<Omit<TerraformShowArgs, "root">> & StepOpts) => NamedActivityStep;
98
+ /**
99
+ * `choudoufuPlanSet` (#4483): `live-plan-set -json` over one live choudoufu
100
+ * root. Hand `step.out.changeSet` from each root's step to `composeChangeSet`
101
+ * to make one change-set document over several roots, such as one per cloud.
102
+ */
103
+ export declare const choudoufuPlanSet: (root: string, opts?: WithStepRefs<Omit<TerraformRootArgs, "root">> & StepOpts) => NamedActivityStep;
98
104
  /**
99
105
  * One run of a pin-bump rollout (#3189). `opts` is
100
106
  * {@link TerraformPinRolloutArgs} itself. It names no root: the rollout finds
@@ -1 +1 @@
1
- {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../src/op/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAExE,kFAAkF;AAClF,KAAK,QAAQ,GAAG;IAAE,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAGF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAGF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC/D,iBAGF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,SACtB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAClE,iBAOF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,SACpB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAChE,iBAOF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,SACvB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KACnE,iBAOF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,QACN,YAAY,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAOF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAOF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,SAAU,YAAY,CAAC,uBAAuB,CAAC,GAAG,QAAQ,KAAG,iBAG5F,CAAC"}
1
+ {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../src/op/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAExE,kFAAkF;AAClF,KAAK,QAAQ,GAAG;IAAE,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAGF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAGF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC/D,iBAGF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,SACtB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAClE,iBAOF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,SACpB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAChE,iBAOF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,SACvB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KACnE,iBAOF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,QACN,YAAY,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAOF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAOF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,SACrB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAGF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,SAAU,YAAY,CAAC,uBAAuB,CAAC,GAAG,QAAQ,KAAG,iBAG5F,CAAC"}
@@ -112,6 +112,15 @@ export const terraformShow = (root, opts) => {
112
112
  const { args, profile, id } = takeProfileAndId(opts);
113
113
  return activity("terraformShow", { root, ...args }, { profile: profile ?? "fastIdempotent", ...(id ? { id } : {}) });
114
114
  };
115
+ /**
116
+ * `choudoufuPlanSet` (#4483): `live-plan-set -json` over one live choudoufu
117
+ * root. Hand `step.out.changeSet` from each root's step to `composeChangeSet`
118
+ * to make one change-set document over several roots, such as one per cloud.
119
+ */
120
+ export const choudoufuPlanSet = (root, opts) => {
121
+ const { args, profile, id } = takeProfileAndId(opts);
122
+ return activity("choudoufuPlanSet", { root, ...args }, { profile: profile ?? "longInfra", ...(id ? { id } : {}) });
123
+ };
115
124
  /**
116
125
  * One run of a pin-bump rollout (#3189). `opts` is
117
126
  * {@link TerraformPinRolloutArgs} itself. It names no root: the rollout finds
@@ -1 +1 @@
1
- {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../src/pin/edit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAGjD,4FAA4F;AAC5F,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,mEAAmE;AACnE,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG;IACpC,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,yGAAyG;IACzG,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAED,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AA2CD;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,CAgDxH"}
1
+ {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../src/pin/edit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAIjD,4FAA4F;AAC5F,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,mEAAmE;AACnE,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG;IACpC,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,yGAAyG;IACzG,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAED,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AA2CD;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,CAiDxH"}
package/dist/pin/edit.js CHANGED
@@ -22,6 +22,7 @@
22
22
  * module (terragucci's `tf-rollout` stage, #3421) decides how it is loaded.
23
23
  */
24
24
  import { movePin } from "./source.js";
25
+ import { isJsonConfigFile, jsonConfigTree } from "../hcl/files.js";
25
26
  import { escapeHcl, scanBlocks, unescapeHcl } from "./scan.js";
26
27
  /** Whether a file is read as a Terragrunt configuration rather than a `.tf` file. */
27
28
  export function isTerragruntFile(file) {
@@ -58,6 +59,8 @@ function sites(tree, blocks, terragrunt) {
58
59
  * is reported with its reason and left as it was.
59
60
  */
60
61
  export async function editPins(text, file, request, parser) {
62
+ if (isJsonConfigFile(file))
63
+ return jsonPins(text, file, request);
61
64
  const terragrunt = isTerragruntFile(file);
62
65
  const tree = (await parser.parse(file, text));
63
66
  const blocks = scanBlocks(text, file);
@@ -116,3 +119,35 @@ export async function editPins(text, file, request, parser) {
116
119
  function rewriteLiteral(raw, was, value) {
117
120
  return raw === was ? value : escapeHcl(value);
118
121
  }
122
+ /**
123
+ * The calls of the module in a `.tf.json` or `.tofu.json` file (chant#4457).
124
+ * The pin edit splices HCL text and has no splice for JSON syntax, so a call
125
+ * that would move is refused by name and the file is left as it is; a call
126
+ * already at the new pin, or at another one, reads as it does in HCL.
127
+ */
128
+ function jsonPins(text, file, request) {
129
+ const calls = [];
130
+ let tree;
131
+ try {
132
+ tree = jsonConfigTree(text);
133
+ }
134
+ catch (err) {
135
+ return { content: text, calls: [{ outcome: "refused", reason: `not JSON the binary reads: ${err instanceof Error ? err.message : String(err)}`, file, call: "(file)" }] };
136
+ }
137
+ const modules = (tree.module && typeof tree.module === "object" ? tree.module : {});
138
+ for (const [name, bodies] of Object.entries(modules)) {
139
+ for (const body of Array.isArray(bodies) ? bodies : []) {
140
+ if (typeof body.source !== "string")
141
+ continue;
142
+ const move = movePin(body.source, typeof body.version === "string" ? body.version : undefined, request);
143
+ if (!move)
144
+ continue;
145
+ const at = { file, call: `module.${name}` };
146
+ if (move.outcome === "moved")
147
+ calls.push({ outcome: "refused", reason: "the call is in Terraform's JSON syntax, which the pin edit does not write; move its pin by hand", ...at });
148
+ else
149
+ calls.push({ ...move, ...at });
150
+ }
151
+ }
152
+ return { content: text, calls };
153
+ }
@@ -70,6 +70,8 @@ export interface PinRolloutOptions extends PinRequest {
70
70
  editTs?: TsPinEditor;
71
71
  /** Markdown appended to a wave's PR body, such as the grouped plan summary of #3188 once a caller has the wave's plans. */
72
72
  planSummary?: (wave: PinWave) => Promise<string | undefined>;
73
+ /** The binary the roots run: `tofu` or `choudoufu` reads `.tofu` files too (chant#4457). Default `terraform`. */
74
+ binary?: string;
73
75
  }
74
76
  /** Where one root's pin stood on the base branch. */
75
77
  export interface PinRootState {
@@ -1 +1 @@
1
- {"version":3,"file":"rollout.d.ts","sourceRoot":"","sources":["../../src/pin/rollout.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rollout.d.ts","sourceRoot":"","sources":["../../src/pin/rollout.ts"],"names":[],"mappings":"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"}
@@ -45,6 +45,7 @@ import { editPins } from "./edit.js";
45
45
  import { checkPinRequest, moduleOf } from "./source.js";
46
46
  import { planPinWaves, restrictWaves, terragruntDependencies } from "./waves.js";
47
47
  import { ghPinForge } from "./forge.js";
48
+ import { isConfigFileFor, listConfigFilesSafe } from "../hcl/files.js";
48
49
  const execFileAsync = /* @__PURE__ */ promisify(execFile);
49
50
  // `gh pr list/create` and `gh api repos/{owner}/{repo}/...` name no host, so
50
51
  // a `gh` call is told the job's forge or `origin`'s host (core's `ghHostEnv`,
@@ -73,22 +74,24 @@ function slug(value) {
73
74
  export function pinWaveBranch(request, wave) {
74
75
  return `chant/pin/${slug(request.module)}/${slug(request.to)}/wave-${wave}`;
75
76
  }
76
- /** The files the pin edit reads in a root directory: its `.tf` files and its `terragrunt.hcl`. */
77
- function rootFiles(worktree, root) {
77
+ /**
78
+ * The files the pin edit reads in a root directory: the configuration files
79
+ * the root's binary reads (the lexicon's one lister, chant#4457) and its
80
+ * `terragrunt.hcl`.
81
+ */
82
+ function rootFiles(worktree, root, binary) {
78
83
  const dir = join(worktree, root);
79
84
  if (!existsSync(dir))
80
85
  return [];
81
- return readdirSync(dir)
82
- .filter((f) => f.endsWith(".tf") || f === "terragrunt.hcl")
83
- .sort()
84
- .map((f) => posix.join(root, f));
86
+ const names = [...listConfigFilesSafe(dir, binary), ...(existsSync(join(dir, "terragrunt.hcl")) ? ["terragrunt.hcl"] : [])];
87
+ return names.sort().map((f) => posix.join(root, f));
85
88
  }
86
- /** Every directory under the worktree with a `.tf` file or a `terragrunt.hcl`. */
87
- function candidateRoots(worktree) {
89
+ /** Every directory under the worktree with a configuration file the binary reads, or a `terragrunt.hcl`. */
90
+ function candidateRoots(worktree, binary) {
88
91
  const out = [];
89
92
  const walk = (rel) => {
90
93
  const entries = readdirSync(join(worktree, rel), { withFileTypes: true });
91
- if (entries.some((e) => e.isFile() && (e.name.endsWith(".tf") || e.name === "terragrunt.hcl")))
94
+ if (entries.some((e) => e.isFile() && (isConfigFileFor(e.name, binary) || e.name === "terragrunt.hcl")))
92
95
  out.push(rel || ".");
93
96
  for (const e of entries)
94
97
  if (e.isDirectory() && !SKIP_DIRS.has(e.name))
@@ -108,7 +111,7 @@ async function editRoot(worktree, root, request, options) {
108
111
  }
109
112
  }
110
113
  else {
111
- files = rootFiles(worktree, root.root);
114
+ files = rootFiles(worktree, root.root, options.binary);
112
115
  }
113
116
  for (const file of files) {
114
117
  const path = join(worktree, file);
@@ -216,7 +219,7 @@ export async function runPinRollout(options) {
216
219
  await run("git", ["worktree", "add", "--quiet", "--detach", worktree, baseRef], repo);
217
220
  try {
218
221
  // 1. Every root's pin on the base branch.
219
- const declared = (options.roots ?? candidateRoots(worktree).map((root) => ({ root }))).map((r) => ({
222
+ const declared = (options.roots ?? candidateRoots(worktree, options.binary).map((root) => ({ root }))).map((r) => ({
220
223
  ...r,
221
224
  root: dirOf(r.root),
222
225
  ...(r.dependsOn ? { dependsOn: r.dependsOn.map(dirOf) } : {}),