@intentius/chant-lexicon-terraform 0.124.52 → 0.124.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (641) hide show
  1. package/README.md +14 -10
  2. package/dist/__fixtures__/pr-loop/estate.component.d.ts +7 -0
  3. package/dist/__fixtures__/pr-loop/estate.component.d.ts.map +1 -0
  4. package/dist/__fixtures__/pr-loop/estate.component.js +7 -0
  5. package/dist/__fixtures__/report/generate.d.ts +26 -0
  6. package/dist/__fixtures__/report/generate.d.ts.map +1 -0
  7. package/dist/__fixtures__/report/generate.js +58 -0
  8. package/dist/applied-state.d.ts +27 -0
  9. package/dist/applied-state.d.ts.map +1 -0
  10. package/dist/applied-state.js +44 -0
  11. package/dist/backend-config.d.ts +53 -0
  12. package/dist/backend-config.d.ts.map +1 -0
  13. package/dist/backend-config.js +136 -0
  14. package/dist/behaviour/kinds.d.ts.map +1 -1
  15. package/dist/behaviour/kinds.js +4 -0
  16. package/dist/behaviour/predict.d.ts.map +1 -1
  17. package/dist/behaviour/predict.js +6 -2
  18. package/dist/behaviour/reference-catalog.d.ts +16 -5
  19. package/dist/behaviour/reference-catalog.d.ts.map +1 -1
  20. package/dist/behaviour/reference-catalog.js +16 -5
  21. package/dist/behaviour/request.d.ts.map +1 -1
  22. package/dist/behaviour/request.js +3 -2
  23. package/dist/changed-roots.d.ts +6 -4
  24. package/dist/changed-roots.d.ts.map +1 -1
  25. package/dist/changed-roots.js +14 -18
  26. package/dist/ci-setup.d.ts +11 -0
  27. package/dist/ci-setup.d.ts.map +1 -0
  28. package/dist/ci-setup.js +41 -0
  29. package/dist/commands.d.ts.map +1 -1
  30. package/dist/commands.js +4 -3
  31. package/dist/components/capability-plugin.d.ts +105 -9
  32. package/dist/components/capability-plugin.d.ts.map +1 -1
  33. package/dist/components/capability-plugin.js +357 -36
  34. package/dist/components/compose-group.d.ts +145 -0
  35. package/dist/components/compose-group.d.ts.map +1 -0
  36. package/dist/components/compose-group.js +407 -0
  37. package/dist/components/placeholder-preview.d.ts +78 -0
  38. package/dist/components/placeholder-preview.d.ts.map +1 -0
  39. package/dist/components/placeholder-preview.js +254 -0
  40. package/dist/components/release-inputs.d.ts +29 -0
  41. package/dist/components/release-inputs.d.ts.map +1 -0
  42. package/dist/components/release-inputs.js +57 -0
  43. package/dist/components/trace-facts.d.ts +16 -0
  44. package/dist/components/trace-facts.d.ts.map +1 -0
  45. package/dist/components/trace-facts.js +19 -0
  46. package/dist/compose/apply-events.d.ts +46 -0
  47. package/dist/compose/apply-events.d.ts.map +1 -0
  48. package/dist/compose/apply-events.js +77 -0
  49. package/dist/compose/estate.d.ts +194 -0
  50. package/dist/compose/estate.d.ts.map +1 -0
  51. package/dist/compose/estate.js +363 -0
  52. package/dist/compose/hcl.d.ts +99 -0
  53. package/dist/compose/hcl.d.ts.map +1 -0
  54. package/dist/compose/hcl.js +302 -0
  55. package/dist/compose/module-address.d.ts +24 -0
  56. package/dist/compose/module-address.d.ts.map +1 -0
  57. package/dist/compose/module-address.js +43 -0
  58. package/dist/compose/root.d.ts +127 -0
  59. package/dist/compose/root.d.ts.map +1 -0
  60. package/dist/compose/root.js +586 -0
  61. package/dist/compose/run.d.ts +183 -0
  62. package/dist/compose/run.d.ts.map +1 -0
  63. package/dist/compose/run.js +447 -0
  64. package/dist/compose/slice.d.ts +102 -0
  65. package/dist/compose/slice.d.ts.map +1 -0
  66. package/dist/compose/slice.js +249 -0
  67. package/dist/compose/state-address.d.ts +28 -0
  68. package/dist/compose/state-address.d.ts.map +1 -0
  69. package/dist/compose/state-address.js +154 -0
  70. package/dist/compose/state-merge.d.ts +52 -0
  71. package/dist/compose/state-merge.d.ts.map +1 -0
  72. package/dist/compose/state-merge.js +135 -0
  73. package/dist/compose.d.ts +179 -0
  74. package/dist/compose.d.ts.map +1 -0
  75. package/dist/compose.js +482 -0
  76. package/dist/composites/terraform-apply-op.d.ts.map +1 -1
  77. package/dist/composites/terraform-apply-op.js +7 -3
  78. package/dist/cone.d.ts +53 -0
  79. package/dist/cone.d.ts.map +1 -0
  80. package/dist/cone.js +119 -0
  81. package/dist/config.d.ts +59 -13
  82. package/dist/config.d.ts.map +1 -1
  83. package/dist/config.js +43 -9
  84. package/dist/cost-hold.d.ts +27 -0
  85. package/dist/cost-hold.d.ts.map +1 -0
  86. package/dist/cost-hold.js +21 -0
  87. package/dist/describe-resources.d.ts +74 -12
  88. package/dist/describe-resources.d.ts.map +1 -1
  89. package/dist/describe-resources.js +374 -66
  90. package/dist/drift-read.d.ts +33 -0
  91. package/dist/drift-read.d.ts.map +1 -0
  92. package/dist/drift-read.js +243 -0
  93. package/dist/drift-record.d.ts +147 -0
  94. package/dist/drift-record.d.ts.map +1 -0
  95. package/dist/drift-record.js +383 -0
  96. package/dist/drift-rows.d.ts +61 -0
  97. package/dist/drift-rows.d.ts.map +1 -0
  98. package/dist/drift-rows.js +108 -0
  99. package/dist/exit-codes.d.ts +55 -0
  100. package/dist/exit-codes.d.ts.map +1 -0
  101. package/dist/exit-codes.js +48 -0
  102. package/dist/hcl/edges.d.ts.map +1 -1
  103. package/dist/hcl/edges.js +24 -1
  104. package/dist/hcl/files.d.ts +61 -0
  105. package/dist/hcl/files.d.ts.map +1 -0
  106. package/dist/hcl/files.js +221 -0
  107. package/dist/hcl/instances.d.ts +82 -0
  108. package/dist/hcl/instances.d.ts.map +1 -0
  109. package/dist/hcl/instances.js +390 -0
  110. package/dist/hcl/parse.d.ts +81 -9
  111. package/dist/hcl/parse.d.ts.map +1 -1
  112. package/dist/hcl/parse.js +114 -16
  113. package/dist/hcl/roots.d.ts.map +1 -1
  114. package/dist/hcl/roots.js +5 -0
  115. package/dist/hcl/suppressions.d.ts.map +1 -1
  116. package/dist/hcl/suppressions.js +8 -2
  117. package/dist/index.d.ts +13 -2
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +24 -2
  120. package/dist/integrity.json +8 -5
  121. package/dist/lint/audit-catalog.d.ts.map +1 -1
  122. package/dist/lint/audit-catalog.js +27 -3
  123. package/dist/lint/audit-lineage.d.ts +14 -2
  124. package/dist/lint/audit-lineage.d.ts.map +1 -1
  125. package/dist/lint/post-synth/index.d.ts.map +1 -1
  126. package/dist/lint/post-synth/index.js +4 -0
  127. package/dist/lint/post-synth/record-store.d.ts +54 -0
  128. package/dist/lint/post-synth/record-store.d.ts.map +1 -0
  129. package/dist/lint/post-synth/record-store.js +104 -0
  130. package/dist/lint/post-synth/tf001.d.ts +2 -2
  131. package/dist/lint/post-synth/tf001.js +2 -2
  132. package/dist/lint/post-synth/tf024.d.ts +35 -11
  133. package/dist/lint/post-synth/tf024.d.ts.map +1 -1
  134. package/dist/lint/post-synth/tf024.js +92 -20
  135. package/dist/lint/post-synth/tf046.d.ts +64 -0
  136. package/dist/lint/post-synth/tf046.d.ts.map +1 -0
  137. package/dist/lint/post-synth/tf046.js +175 -0
  138. package/dist/lint/post-synth/tf047.d.ts +36 -0
  139. package/dist/lint/post-synth/tf047.d.ts.map +1 -0
  140. package/dist/lint/post-synth/tf047.js +94 -0
  141. package/dist/live-plan-reasons.d.ts +21 -0
  142. package/dist/live-plan-reasons.d.ts.map +1 -0
  143. package/dist/live-plan-reasons.js +47 -0
  144. package/dist/lock-rows.d.ts +36 -0
  145. package/dist/lock-rows.d.ts.map +1 -0
  146. package/dist/lock-rows.js +60 -0
  147. package/dist/lock-timeout.d.ts +22 -0
  148. package/dist/lock-timeout.d.ts.map +1 -0
  149. package/dist/lock-timeout.js +32 -0
  150. package/dist/lsp/option-keys.d.ts.map +1 -1
  151. package/dist/lsp/option-keys.js +2 -0
  152. package/dist/manifest.json +1 -1
  153. package/dist/module-catalog/index.d.ts +69 -0
  154. package/dist/module-catalog/index.d.ts.map +1 -0
  155. package/dist/module-catalog/index.js +110 -0
  156. package/dist/module-catalog/refresh-cli.d.ts +4 -0
  157. package/dist/module-catalog/refresh-cli.d.ts.map +1 -0
  158. package/dist/module-catalog/refresh-cli.js +64 -0
  159. package/dist/module-catalog/static-tier.d.ts +6 -0
  160. package/dist/module-catalog/static-tier.d.ts.map +1 -0
  161. package/dist/module-catalog/static-tier.js +1650 -0
  162. package/dist/module-catalog/types.d.ts +65 -0
  163. package/dist/module-catalog/types.d.ts.map +1 -0
  164. package/dist/module-catalog/types.js +33 -0
  165. package/dist/okf/index.md +3 -1
  166. package/dist/okf/rules/TF024.md +2 -2
  167. package/dist/okf/rules/TF046.md +11 -0
  168. package/dist/okf/rules/TF047.md +11 -0
  169. package/dist/op/activities/index.d.ts +6 -2
  170. package/dist/op/activities/index.d.ts.map +1 -1
  171. package/dist/op/activities/index.js +5 -1
  172. package/dist/op/activities/live-detect.d.ts +4 -3
  173. package/dist/op/activities/live-detect.d.ts.map +1 -1
  174. package/dist/op/activities/live-detect.js +57 -32
  175. package/dist/op/activities/pin-rollout.d.ts +2 -0
  176. package/dist/op/activities/pin-rollout.d.ts.map +1 -1
  177. package/dist/op/activities/pin-rollout.js +1 -0
  178. package/dist/op/activities/terraform.d.ts +182 -5
  179. package/dist/op/activities/terraform.d.ts.map +1 -1
  180. package/dist/op/activities/terraform.js +224 -24
  181. package/dist/op/activity-contracts.d.ts +27 -0
  182. package/dist/op/activity-contracts.d.ts.map +1 -1
  183. package/dist/op/activity-contracts.js +19 -1
  184. package/dist/op/builders.d.ts +7 -1
  185. package/dist/op/builders.d.ts.map +1 -1
  186. package/dist/op/builders.js +9 -0
  187. package/dist/pin/consumers.d.ts +35 -0
  188. package/dist/pin/consumers.d.ts.map +1 -0
  189. package/dist/pin/consumers.js +70 -0
  190. package/dist/pin/edit.d.ts.map +1 -1
  191. package/dist/pin/edit.js +35 -0
  192. package/dist/pin/rollout.d.ts +2 -0
  193. package/dist/pin/rollout.d.ts.map +1 -1
  194. package/dist/pin/rollout.js +14 -11
  195. package/dist/plan-shape.d.ts +30 -0
  196. package/dist/plan-shape.d.ts.map +1 -0
  197. package/dist/plan-shape.js +125 -0
  198. package/dist/plugin.d.ts.map +1 -1
  199. package/dist/plugin.js +37 -18
  200. package/dist/registry/address.d.ts +63 -0
  201. package/dist/registry/address.d.ts.map +1 -0
  202. package/dist/registry/address.js +65 -0
  203. package/dist/registry/catalog-read.d.ts +14 -0
  204. package/dist/registry/catalog-read.d.ts.map +1 -0
  205. package/dist/registry/catalog-read.js +58 -0
  206. package/dist/registry/deprecated.d.ts +37 -0
  207. package/dist/registry/deprecated.d.ts.map +1 -0
  208. package/dist/registry/deprecated.js +43 -0
  209. package/dist/registry/docs.d.ts +96 -0
  210. package/dist/registry/docs.d.ts.map +1 -0
  211. package/dist/registry/docs.js +210 -0
  212. package/dist/registry/edge/edge.d.ts +46 -0
  213. package/dist/registry/edge/edge.d.ts.map +1 -0
  214. package/dist/registry/edge/edge.js +194 -0
  215. package/dist/registry/edge/index.d.ts +11 -0
  216. package/dist/registry/edge/index.d.ts.map +1 -0
  217. package/dist/registry/edge/index.js +10 -0
  218. package/dist/registry/edge/lambda-edge.d.ts +76 -0
  219. package/dist/registry/edge/lambda-edge.d.ts.map +1 -0
  220. package/dist/registry/edge/lambda-edge.js +95 -0
  221. package/dist/registry/edge/sigv4-web.d.ts +33 -0
  222. package/dist/registry/edge/sigv4-web.d.ts.map +1 -0
  223. package/dist/registry/edge/sigv4-web.js +56 -0
  224. package/dist/registry/edge/token-source.d.ts +73 -0
  225. package/dist/registry/edge/token-source.d.ts.map +1 -0
  226. package/dist/registry/edge/token-source.js +115 -0
  227. package/dist/registry/edge/worker.d.ts +45 -0
  228. package/dist/registry/edge/worker.d.ts.map +1 -0
  229. package/dist/registry/edge/worker.js +26 -0
  230. package/dist/registry/handler.d.ts +25 -0
  231. package/dist/registry/handler.d.ts.map +1 -0
  232. package/dist/registry/handler.js +75 -0
  233. package/dist/registry/headers-file.d.ts +21 -0
  234. package/dist/registry/headers-file.d.ts.map +1 -0
  235. package/dist/registry/headers-file.js +57 -0
  236. package/dist/registry/index.d.ts +31 -0
  237. package/dist/registry/index.d.ts.map +1 -0
  238. package/dist/registry/index.js +30 -0
  239. package/dist/registry/mirror-paths.d.ts +9 -0
  240. package/dist/registry/mirror-paths.d.ts.map +1 -0
  241. package/dist/registry/mirror-paths.js +8 -0
  242. package/dist/registry/mirror.d.ts +130 -0
  243. package/dist/registry/mirror.d.ts.map +1 -0
  244. package/dist/registry/mirror.js +319 -0
  245. package/dist/registry/proxies.d.ts +73 -0
  246. package/dist/registry/proxies.d.ts.map +1 -0
  247. package/dist/registry/proxies.js +111 -0
  248. package/dist/registry/reader.d.ts +57 -0
  249. package/dist/registry/reader.d.ts.map +1 -0
  250. package/dist/registry/reader.js +198 -0
  251. package/dist/registry/semver.d.ts +16 -0
  252. package/dist/registry/semver.d.ts.map +1 -0
  253. package/dist/registry/semver.js +24 -0
  254. package/dist/registry/serve.d.ts +24 -0
  255. package/dist/registry/serve.d.ts.map +1 -0
  256. package/dist/registry/serve.js +64 -0
  257. package/dist/registry/source-forms.d.ts +79 -0
  258. package/dist/registry/source-forms.d.ts.map +1 -0
  259. package/dist/registry/source-forms.js +202 -0
  260. package/dist/registry/targets/artifactory.d.ts +28 -0
  261. package/dist/registry/targets/artifactory.d.ts.map +1 -0
  262. package/dist/registry/targets/artifactory.js +206 -0
  263. package/dist/registry/targets/fake-artifactory.d.ts +30 -0
  264. package/dist/registry/targets/fake-artifactory.d.ts.map +1 -0
  265. package/dist/registry/targets/fake-artifactory.js +142 -0
  266. package/dist/registry/targets/fake-gitlab.d.ts +39 -0
  267. package/dist/registry/targets/fake-gitlab.d.ts.map +1 -0
  268. package/dist/registry/targets/fake-gitlab.js +136 -0
  269. package/dist/registry/targets/fake-hcp.d.ts +50 -0
  270. package/dist/registry/targets/fake-hcp.d.ts.map +1 -0
  271. package/dist/registry/targets/fake-hcp.js +202 -0
  272. package/dist/registry/targets/fake-http.d.ts +31 -0
  273. package/dist/registry/targets/fake-http.d.ts.map +1 -0
  274. package/dist/registry/targets/fake-http.js +87 -0
  275. package/dist/registry/targets/fake-nexus.d.ts +27 -0
  276. package/dist/registry/targets/fake-nexus.d.ts.map +1 -0
  277. package/dist/registry/targets/fake-nexus.js +137 -0
  278. package/dist/registry/targets/gitlab.d.ts +53 -0
  279. package/dist/registry/targets/gitlab.d.ts.map +1 -0
  280. package/dist/registry/targets/gitlab.js +187 -0
  281. package/dist/registry/targets/hcp.d.ts +29 -0
  282. package/dist/registry/targets/hcp.d.ts.map +1 -0
  283. package/dist/registry/targets/hcp.js +310 -0
  284. package/dist/registry/targets/hosted.d.ts +62 -0
  285. package/dist/registry/targets/hosted.d.ts.map +1 -0
  286. package/dist/registry/targets/hosted.js +74 -0
  287. package/dist/registry/targets/index.d.ts +18 -0
  288. package/dist/registry/targets/index.d.ts.map +1 -0
  289. package/dist/registry/targets/index.js +17 -0
  290. package/dist/registry/targets/module-target.d.ts +68 -0
  291. package/dist/registry/targets/module-target.d.ts.map +1 -0
  292. package/dist/registry/targets/module-target.js +63 -0
  293. package/dist/registry/targets/nexus.d.ts +43 -0
  294. package/dist/registry/targets/nexus.d.ts.map +1 -0
  295. package/dist/registry/targets/nexus.js +250 -0
  296. package/dist/registry/targets/zip.d.ts +55 -0
  297. package/dist/registry/targets/zip.d.ts.map +1 -0
  298. package/dist/registry/targets/zip.js +278 -0
  299. package/dist/registry/writer.d.ts +101 -0
  300. package/dist/registry/writer.d.ts.map +1 -0
  301. package/dist/registry/writer.js +109 -0
  302. package/dist/report.d.ts +15 -6
  303. package/dist/report.d.ts.map +1 -1
  304. package/dist/report.js +28 -55
  305. package/dist/rules/record-store.ts +122 -0
  306. package/dist/rules/tf001.ts +2 -2
  307. package/dist/rules/tf024.ts +105 -22
  308. package/dist/rules/tf046.ts +204 -0
  309. package/dist/rules/tf047.ts +114 -0
  310. package/dist/skills/chant-terraform.md +1 -1
  311. package/dist/state/azure-blob.d.ts +9 -0
  312. package/dist/state/azure-blob.d.ts.map +1 -0
  313. package/dist/state/azure-blob.js +8 -0
  314. package/dist/state/backend.d.ts +158 -0
  315. package/dist/state/backend.d.ts.map +1 -0
  316. package/dist/state/backend.js +406 -0
  317. package/dist/state/exec.d.ts +39 -0
  318. package/dist/state/exec.d.ts.map +1 -0
  319. package/dist/state/exec.js +80 -0
  320. package/dist/state/gcs.d.ts +8 -0
  321. package/dist/state/gcs.d.ts.map +1 -0
  322. package/dist/state/gcs.js +7 -0
  323. package/dist/state/index.d.ts +15 -0
  324. package/dist/state/index.d.ts.map +1 -0
  325. package/dist/state/index.js +14 -0
  326. package/dist/state/locks.d.ts +105 -0
  327. package/dist/state/locks.d.ts.map +1 -0
  328. package/dist/state/locks.js +130 -0
  329. package/dist/state/object-store.d.ts +8 -0
  330. package/dist/state/object-store.d.ts.map +1 -0
  331. package/dist/state/object-store.js +7 -0
  332. package/dist/state/root-states.d.ts +131 -0
  333. package/dist/state/root-states.d.ts.map +1 -0
  334. package/dist/state/root-states.js +241 -0
  335. package/dist/state/s3.d.ts +21 -0
  336. package/dist/state/s3.d.ts.map +1 -0
  337. package/dist/state/s3.js +19 -0
  338. package/dist/state/store.d.ts +51 -0
  339. package/dist/state/store.d.ts.map +1 -0
  340. package/dist/state/store.js +185 -0
  341. package/dist/state-ops.d.ts +255 -0
  342. package/dist/state-ops.d.ts.map +1 -0
  343. package/dist/state-ops.js +305 -0
  344. package/dist/state-reads.d.ts +57 -0
  345. package/dist/state-reads.d.ts.map +1 -0
  346. package/dist/state-reads.js +104 -0
  347. package/dist/terragrunt/affected.d.ts.map +1 -1
  348. package/dist/terragrunt/affected.js +2 -1
  349. package/dist/used-sources.d.ts +54 -0
  350. package/dist/used-sources.d.ts.map +1 -0
  351. package/dist/used-sources.js +157 -0
  352. package/package.json +24 -4
  353. package/src/__fixtures__/ACCEPTANCE.md +69 -7
  354. package/src/__fixtures__/compose/apply-failed-in-app.jsonl +12 -0
  355. package/src/__fixtures__/compose/state-app.json +66 -0
  356. package/src/__fixtures__/compose/state-edge.json +40 -0
  357. package/src/__fixtures__/compose/state-net.json +58 -0
  358. package/src/__fixtures__/compose/three-app.plan.json +184 -0
  359. package/src/__fixtures__/compose/three-composed.plan.json +509 -0
  360. package/src/__fixtures__/compose/three-edge.plan.json +144 -0
  361. package/src/__fixtures__/compose/three-net.plan.json +174 -0
  362. package/src/__fixtures__/compose/unknown-composed.plan.json +228 -0
  363. package/src/__fixtures__/compose/working-after-apply.json +145 -0
  364. package/src/__fixtures__/config-blocks/app/main.tf +61 -0
  365. package/src/__fixtures__/config-blocks/app/modules/network/main.tf +4 -0
  366. package/src/__fixtures__/config-blocks/app-json/main.tf.json +39 -0
  367. package/src/__fixtures__/config-files/shadow/main.tf +3 -0
  368. package/src/__fixtures__/config-files/shadow/main.tofu +3 -0
  369. package/src/__fixtures__/config-files/shadow/other.tf +3 -0
  370. package/src/__fixtures__/config-files/tf/main.tf +11 -0
  371. package/src/__fixtures__/config-files/tf-json/main.tf.json +18 -0
  372. package/src/__fixtures__/config-files/tofu/main.tofu +11 -0
  373. package/src/__fixtures__/config-files/tofu-json/main.tofu.json +18 -0
  374. package/src/__fixtures__/config-files/tofu-only/main.tofu +3 -0
  375. package/src/__fixtures__/drift-record/README.md +16 -0
  376. package/src/__fixtures__/drift-record/live-plan-sections.json +27 -0
  377. package/src/__fixtures__/drift-rows/live-root-plan.json +70 -0
  378. package/src/__fixtures__/drift-rows/refresh-plan.json +61 -0
  379. package/src/__fixtures__/instance-forms/app/main.tf +110 -0
  380. package/src/__fixtures__/instance-forms/app/modules/svc/main.tf +3 -0
  381. package/src/__fixtures__/instance-vars/app/main.tf +52 -0
  382. package/src/__fixtures__/instance-vars/app/modules/app/main.tf +12 -0
  383. package/src/__fixtures__/instance-vars/app/modules/edge/main.tf +3 -0
  384. package/src/__fixtures__/instance-vars/app/terraform.tfvars +1 -0
  385. package/src/__fixtures__/live-module-keyed/live-graph.json +259 -0
  386. package/src/__fixtures__/live-module-keyed/live-plan.json +87 -0
  387. package/src/__fixtures__/live-module-keyed/root/estate.chdf.hcl +1 -0
  388. package/src/__fixtures__/live-module-keyed/root/main.tf +21 -0
  389. package/src/__fixtures__/live-module-keyed/root/modules/app/main.tf +17 -0
  390. package/src/__fixtures__/live-module-keyed/root/modules/edge/main.tf +7 -0
  391. package/src/__fixtures__/live-module-keyed/root/versions.tf +30 -0
  392. package/src/__fixtures__/live-module-keyed/show-state.json +146 -0
  393. package/src/__fixtures__/pr-loop/README.md +50 -0
  394. package/src/__fixtures__/pr-loop/chant.config.json +13 -0
  395. package/src/__fixtures__/pr-loop/estate.component.ts +10 -0
  396. package/src/__fixtures__/pr-loop/roots/a/main.tf +11 -0
  397. package/src/__fixtures__/pr-loop/roots/app/main.tf +19 -0
  398. package/src/__fixtures__/pr-loop/roots/b/main.tf +7 -0
  399. package/src/__fixtures__/pr-loop/roots/dns/main.tf +3 -0
  400. package/src/__fixtures__/pr-loop/roots/net/main.tf +7 -0
  401. package/src/__fixtures__/report/applied.show.json +51 -0
  402. package/src/__fixtures__/report/generate.ts +63 -0
  403. package/src/__fixtures__/report/plan.show.json +200 -0
  404. package/src/__fixtures__/report/tf-apply/change-set.json +170 -0
  405. package/src/__fixtures__/report/tf-apply/report.json +338 -0
  406. package/src/__fixtures__/report/tf-plan/change-set.json +170 -0
  407. package/src/__fixtures__/report/tf-plan/report.json +320 -0
  408. package/src/__fixtures__/three-cloud-azure.graph.json +290 -0
  409. package/src/__fixtures__/used-sources/root/extra.tofu +1 -0
  410. package/src/__fixtures__/used-sources/root/main.tf +31 -0
  411. package/src/__fixtures__/used-sources/root/modules/net/main.tf +14 -0
  412. package/src/__fixtures__/used-sources/root/modules/net/versions.tf.json +7 -0
  413. package/src/__fixtures__/used-sources/unit/terragrunt.hcl +3 -0
  414. package/src/applied-state.ts +52 -0
  415. package/src/backend-config.test.ts +105 -0
  416. package/src/backend-config.ts +133 -0
  417. package/src/behaviour/kinds.ts +4 -0
  418. package/src/behaviour/predict.ts +5 -2
  419. package/src/behaviour/reference-catalog.plugin.test.ts +65 -0
  420. package/src/behaviour/reference-catalog.ts +16 -5
  421. package/src/behaviour/request.ts +3 -2
  422. package/src/change-set-instances.test.ts +98 -0
  423. package/src/changed-roots.ts +14 -16
  424. package/src/ci-setup.ts +42 -0
  425. package/src/commands.ts +4 -3
  426. package/src/components/capability-plugin.test.ts +198 -19
  427. package/src/components/capability-plugin.ts +383 -35
  428. package/src/components/change-set-graph.e2e.test.ts +11 -5
  429. package/src/components/compose-group.test.ts +486 -0
  430. package/src/components/compose-group.ts +489 -0
  431. package/src/components/demo-composed-roots.e2e.test.ts +440 -0
  432. package/src/components/member-outputs.test.ts +76 -0
  433. package/src/components/placeholder-preview.test.ts +147 -0
  434. package/src/components/placeholder-preview.ts +257 -0
  435. package/src/components/pr-loop-compose.tofu.e2e.test.ts +363 -0
  436. package/src/components/pr-loop.tofu.test.ts +113 -57
  437. package/src/components/release-inputs.ts +69 -0
  438. package/src/components/trace-facts.test.ts +21 -0
  439. package/src/components/trace-facts.ts +27 -0
  440. package/src/compose/apply-events.test.ts +62 -0
  441. package/src/compose/apply-events.ts +100 -0
  442. package/src/compose/estate.choudoufu.e2e.test.ts +112 -0
  443. package/src/compose/estate.test.ts +421 -0
  444. package/src/compose/estate.ts +443 -0
  445. package/src/compose/hcl.ts +311 -0
  446. package/src/compose/module-address.ts +49 -0
  447. package/src/compose/root.test.ts +416 -0
  448. package/src/compose/root.ts +592 -0
  449. package/src/compose/run.test.ts +440 -0
  450. package/src/compose/run.tofu.test.ts +176 -0
  451. package/src/compose/run.ts +527 -0
  452. package/src/compose/slice.test.ts +184 -0
  453. package/src/compose/slice.ts +290 -0
  454. package/src/compose/state-address.ts +157 -0
  455. package/src/compose/state-merge.test.ts +131 -0
  456. package/src/compose/state-merge.ts +148 -0
  457. package/src/compose.test.ts +248 -0
  458. package/src/compose.ts +627 -0
  459. package/src/composites/terraform-apply-op.test.ts +9 -0
  460. package/src/composites/terraform-apply-op.ts +7 -3
  461. package/src/cone.ts +138 -0
  462. package/src/config-file-listers.test.ts +265 -0
  463. package/src/config.ts +44 -9
  464. package/src/cost-hold.ts +29 -0
  465. package/src/describe-resources.engine-facts.test.ts +411 -0
  466. package/src/describe-resources.live-module-keyed.test.ts +290 -0
  467. package/src/describe-resources.ts +416 -70
  468. package/src/drift-read.ts +284 -0
  469. package/src/drift-record.test.ts +223 -0
  470. package/src/drift-record.ts +455 -0
  471. package/src/drift-rows.test.ts +135 -0
  472. package/src/drift-rows.ts +137 -0
  473. package/src/exit-codes.test.ts +23 -0
  474. package/src/exit-codes.ts +59 -0
  475. package/src/hcl/config-blocks.test.ts +174 -0
  476. package/src/hcl/edges.ts +21 -1
  477. package/src/hcl/files.ts +234 -0
  478. package/src/hcl/instance-forms.test.ts +94 -0
  479. package/src/hcl/instances.test.ts +137 -0
  480. package/src/hcl/instances.ts +422 -0
  481. package/src/hcl/parse.test.ts +139 -1
  482. package/src/hcl/parse.ts +149 -16
  483. package/src/hcl/roots.ts +6 -0
  484. package/src/hcl/suppressions.ts +8 -2
  485. package/src/index.ts +79 -0
  486. package/src/lint/audit-catalog.ts +29 -3
  487. package/src/lint/audit-lineage.ts +14 -2
  488. package/src/lint/post-synth/fixtures/TF024/live-with-record-store/main.tf +18 -0
  489. package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/estate.chdf.hcl +1 -0
  490. package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/main.tf +10 -0
  491. package/src/lint/post-synth/fixtures/TF046/azurerm-no-resource-group/main.tf +15 -0
  492. package/src/lint/post-synth/fixtures/TF046/backend-config-only/main.tf +14 -0
  493. package/src/lint/post-synth/fixtures/TF046/derived-s3/backend.tf +7 -0
  494. package/src/lint/post-synth/fixtures/TF046/derived-s3/main.tf +9 -0
  495. package/src/lint/post-synth/fixtures/TF046/explicit-local/main.tf +16 -0
  496. package/src/lint/post-synth/fixtures/TF046/explicit-s3-beside-local-backend/main.tf +15 -0
  497. package/src/lint/post-synth/fixtures/TF046/implied-local/main.tf +9 -0
  498. package/src/lint/post-synth/fixtures/TF046/sidecar-implied/estate.chdf.hcl +1 -0
  499. package/src/lint/post-synth/fixtures/TF046/sidecar-implied/main.tf +5 -0
  500. package/src/lint/post-synth/fixtures/TF047/clear/main.tf +12 -0
  501. package/src/lint/post-synth/fixtures/TF047/refused/main.tf +12 -0
  502. package/src/lint/post-synth/fixtures/TF047/unknown-module/main.tf +12 -0
  503. package/src/lint/post-synth/fixtures/TF047/unknown-version/main.tf +12 -0
  504. package/src/lint/post-synth/index.ts +4 -0
  505. package/src/lint/post-synth/record-store.ts +122 -0
  506. package/src/lint/post-synth/tf001.ts +2 -2
  507. package/src/lint/post-synth/tf024.test.ts +26 -5
  508. package/src/lint/post-synth/tf024.ts +105 -22
  509. package/src/lint/post-synth/tf046.test.ts +137 -0
  510. package/src/lint/post-synth/tf046.ts +204 -0
  511. package/src/lint/post-synth/tf047.test.ts +56 -0
  512. package/src/lint/post-synth/tf047.ts +114 -0
  513. package/src/live-plan-reasons.ts +54 -0
  514. package/src/lock-rows.test.ts +70 -0
  515. package/src/lock-rows.ts +82 -0
  516. package/src/lock-timeout.ts +34 -0
  517. package/src/lsp/completions.test.ts +1 -1
  518. package/src/lsp/option-keys.ts +2 -0
  519. package/src/module-catalog/index.test.ts +93 -0
  520. package/src/module-catalog/index.ts +159 -0
  521. package/src/module-catalog/refresh-cli.ts +68 -0
  522. package/src/module-catalog/static-tier.ts +1659 -0
  523. package/src/module-catalog/types.ts +88 -0
  524. package/src/op/activities/choudoufu-plan-set-op.test.ts +111 -0
  525. package/src/op/activities/index.ts +9 -0
  526. package/src/op/activities/live-detect.ts +55 -26
  527. package/src/op/activities/pin-rollout.ts +3 -0
  528. package/src/op/activities/registry.test.ts +2 -1
  529. package/src/op/activities/terraform.test.ts +55 -0
  530. package/src/op/activities/terraform.ts +312 -27
  531. package/src/op/activity-contracts.test.ts +1 -0
  532. package/src/op/activity-contracts.ts +24 -1
  533. package/src/op/builders.ts +14 -0
  534. package/src/op-wave-step.test.ts +109 -0
  535. package/src/pin/__fixtures__/consumers/live/dev/main.tf +4 -0
  536. package/src/pin/__fixtures__/consumers/live/prod/main.tf +21 -0
  537. package/src/pin/__fixtures__/consumers/units/vpc/terragrunt.hcl +7 -0
  538. package/src/pin/bundle.test.ts +2 -0
  539. package/src/pin/consumers.test.ts +57 -0
  540. package/src/pin/consumers.ts +95 -0
  541. package/src/pin/edit.ts +30 -0
  542. package/src/pin/rollout.ts +16 -11
  543. package/src/plan-shape.test.ts +148 -0
  544. package/src/plan-shape.ts +128 -0
  545. package/src/plugin.ts +39 -19
  546. package/src/registry/__fixtures__/docs/documented/.terraform-docs.yml +3 -0
  547. package/src/registry/__fixtures__/docs/documented/README.md +1 -0
  548. package/src/registry/__fixtures__/docs/documented/main.tf +8 -0
  549. package/src/registry/__fixtures__/docs/json/main.tf.json +14 -0
  550. package/src/registry/__fixtures__/docs/vpc/README.md +3 -0
  551. package/src/registry/__fixtures__/docs/vpc/examples/complete/main.tf +5 -0
  552. package/src/registry/__fixtures__/docs/vpc/examples/simple/README.md +1 -0
  553. package/src/registry/__fixtures__/docs/vpc/examples/simple/main.tf +4 -0
  554. package/src/registry/__fixtures__/docs/vpc/main.tf +14 -0
  555. package/src/registry/__fixtures__/docs/vpc/outputs.tf +9 -0
  556. package/src/registry/__fixtures__/docs/vpc/variables.tf +16 -0
  557. package/src/registry/__fixtures__/proxied-root/main.tf +12 -0
  558. package/src/registry/address.ts +100 -0
  559. package/src/registry/catalog-read.ts +56 -0
  560. package/src/registry/deprecated.test.ts +139 -0
  561. package/src/registry/deprecated.ts +76 -0
  562. package/src/registry/docs.test.ts +140 -0
  563. package/src/registry/docs.ts +299 -0
  564. package/src/registry/edge/bundle.test.ts +50 -0
  565. package/src/registry/edge/edge.test.ts +176 -0
  566. package/src/registry/edge/edge.ts +211 -0
  567. package/src/registry/edge/index.ts +10 -0
  568. package/src/registry/edge/lambda-edge.ts +127 -0
  569. package/src/registry/edge/sigv4-web.ts +79 -0
  570. package/src/registry/edge/token-source.test.ts +258 -0
  571. package/src/registry/edge/token-source.ts +169 -0
  572. package/src/registry/edge/worker.ts +61 -0
  573. package/src/registry/handler.ts +89 -0
  574. package/src/registry/headers-file.ts +61 -0
  575. package/src/registry/index.ts +31 -0
  576. package/src/registry/mirror-paths.ts +10 -0
  577. package/src/registry/mirror.test.ts +198 -0
  578. package/src/registry/mirror.ts +395 -0
  579. package/src/registry/proxies.test.ts +153 -0
  580. package/src/registry/proxies.ts +132 -0
  581. package/src/registry/reader.ts +225 -0
  582. package/src/registry/registry.test.ts +262 -0
  583. package/src/registry/semver.ts +34 -0
  584. package/src/registry/serve.ts +82 -0
  585. package/src/registry/source-forms.test.ts +87 -0
  586. package/src/registry/source-forms.ts +216 -0
  587. package/src/registry/targets/artifactory.test.ts +248 -0
  588. package/src/registry/targets/artifactory.ts +257 -0
  589. package/src/registry/targets/fake-artifactory.ts +158 -0
  590. package/src/registry/targets/fake-gitlab.ts +161 -0
  591. package/src/registry/targets/fake-hcp.ts +233 -0
  592. package/src/registry/targets/fake-http.ts +97 -0
  593. package/src/registry/targets/fake-nexus.ts +152 -0
  594. package/src/registry/targets/gitlab.test.ts +208 -0
  595. package/src/registry/targets/gitlab.ts +223 -0
  596. package/src/registry/targets/hcp.test.ts +230 -0
  597. package/src/registry/targets/hcp.ts +360 -0
  598. package/src/registry/targets/hosted.ts +117 -0
  599. package/src/registry/targets/index.ts +17 -0
  600. package/src/registry/targets/module-target.ts +131 -0
  601. package/src/registry/targets/nexus.test.ts +220 -0
  602. package/src/registry/targets/nexus.ts +314 -0
  603. package/src/registry/targets/zip.test.ts +65 -0
  604. package/src/registry/targets/zip.ts +287 -0
  605. package/src/registry/writer.ts +155 -0
  606. package/src/report.test.ts +82 -1
  607. package/src/report.ts +31 -54
  608. package/src/signal-only-root.test.ts +72 -0
  609. package/src/skills/chant-terraform.md +1 -1
  610. package/src/state/azure-blob.ts +8 -0
  611. package/src/state/backend-verdict.test.ts +187 -0
  612. package/src/state/backend.test.ts +127 -0
  613. package/src/state/backend.ts +488 -0
  614. package/src/state/exec.ts +111 -0
  615. package/src/state/gcs.ts +7 -0
  616. package/src/state/index.ts +15 -0
  617. package/src/state/locks.test.ts +84 -0
  618. package/src/state/locks.ts +201 -0
  619. package/src/state/object-store.ts +7 -0
  620. package/src/state/root-states-floci.e2e.test.ts +66 -0
  621. package/src/state/root-states.test.ts +462 -0
  622. package/src/state/root-states.ts +309 -0
  623. package/src/state/s3.ts +21 -0
  624. package/src/state/store.ts +225 -0
  625. package/src/state-ops.test.ts +182 -0
  626. package/src/state-ops.ts +462 -0
  627. package/src/state-reads.ts +116 -0
  628. package/src/terragrunt/affected.ts +2 -1
  629. package/src/used-sources.test.ts +75 -0
  630. package/src/used-sources.ts +204 -0
  631. package/src/workspace-kinds.test.ts +4 -17
  632. package/workspace-kinds.json +1 -24
  633. package/dist/workspace-member.d.ts +0 -30
  634. package/dist/workspace-member.d.ts.map +0 -1
  635. package/dist/workspace-member.js +0 -91
  636. package/schemas/pulumi.schema.json +0 -34
  637. package/src/testdata/pulumi-member/Pulumi.yaml +0 -2
  638. package/src/testdata/pulumi-member/stack-export.json +0 -53
  639. package/src/testdata/pulumi-unexported/Pulumi.yaml +0 -2
  640. package/src/workspace-member.test.ts +0 -89
  641. package/src/workspace-member.ts +0 -126
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Module sources and provider addresses in a normal form (chant#3924), beside
3
+ * the registry address parsing of ./address.ts (chant#4565), so a product's
4
+ * allow list, the catalog's consumer scan and the proxy forms (chant#4606)
5
+ * compare sources one way. No Node imports, so an edge function can carry it.
6
+ *
7
+ * A module source is compared without its `?` query: a `ref`, `version` or
8
+ * `tag` pins a release and does not move where the code comes from. A
9
+ * registry source written without a host is read by Terraform from
10
+ * registry.terraform.io and by OpenTofu from registry.opentofu.org, so it
11
+ * stands for both; Terragrunt's `tfr:///` and `tfr://<host>/` are the same
12
+ * sources. Registry shapes follow `classifyModuleSource`
13
+ * (../lint/post-synth/module-source.ts), which mirrors
14
+ * hashicorp/terraform-registry-address.
15
+ *
16
+ * A provider address is `<host>/<namespace>/<type>` in lower case. A
17
+ * `required_providers` source without a host gets the binary's default
18
+ * registry, one with only a type gets the `hashicorp` namespace (Terraform's
19
+ * legacy reading), and a provider a module uses with no entry at all is the
20
+ * one `init` installs for it, `hashicorp/<name>`; the binary's own
21
+ * `terraform` provider is never downloaded and has no address.
22
+ *
23
+ * With `proxies` (chant#4606, ./proxies.ts), a source written through an
24
+ * Artifactory remote repository or a Nexus proxy host block stands for its
25
+ * upstream source: its forms include the upstream's, and its normal form
26
+ * and provider address are the upstream's, so it compares equal to the
27
+ * plain source.
28
+ */
29
+ import { classifyModuleSource } from "../lint/post-synth/module-source";
30
+ import { parseRegistrySource } from "./address";
31
+ import { moduleThroughProxy, providerThroughProxy, type RegistryProxy } from "./proxies";
32
+
33
+ /** The public registry's host, which a source with no host names under Terraform. */
34
+ export const PUBLIC_REGISTRY_HOST = "registry.terraform.io";
35
+ /** OpenTofu's public registry's host. */
36
+ export const OPENTOFU_REGISTRY_HOST = "registry.opentofu.org";
37
+ /** The hosts a provider or registry module address written without one is read from, Terraform's first. */
38
+ export const DEFAULT_REGISTRIES: readonly string[] = [PUBLIC_REGISTRY_HOST, OPENTOFU_REGISTRY_HOST];
39
+
40
+ /** Which binary reads an address: it decides the default registry host. */
41
+ export type SourceBinary = "terraform" | "tofu";
42
+
43
+ /** The default registry host for a binary. */
44
+ export const defaultRegistryHost = (binary: SourceBinary = "terraform"): string => (binary === "tofu" ? OPENTOFU_REGISTRY_HOST : PUBLIC_REGISTRY_HOST);
45
+
46
+ /** How a source is read: the binary (for default hosts) and the registry proxies in front of upstreams. */
47
+ export interface SourceFormOptions {
48
+ binary?: SourceBinary;
49
+ proxies?: readonly RegistryProxy[];
50
+ }
51
+
52
+ /** A binary alone, or the options. */
53
+ export type SourceFormArg = SourceBinary | SourceFormOptions;
54
+
55
+ const optionsOf = (o: SourceFormArg | undefined): SourceFormOptions => (typeof o === "string" ? { binary: o } : (o ?? {}));
56
+
57
+ /** A source without its `?` query, trimmed. */
58
+ export const withoutQuery = (source: string): string => source.trim().split("?")[0]!;
59
+
60
+ /** A hostless registry module source (`<ns>/<name>/<system>`, optionally `//<subdir>`), as the registry protocol shapes one. */
61
+ function isHostlessModule(source: string): boolean {
62
+ const base = source.split("//")[0]!;
63
+ return base.split("/").length === 3 && classifyModuleSource(base).kind === "registry";
64
+ }
65
+
66
+ /** The source without a default registry's host, when it starts with one. */
67
+ function atDefaultRegistry(source: string): string | undefined {
68
+ const host = DEFAULT_REGISTRIES.find((h) => source.toLowerCase().startsWith(`${h}/`));
69
+ return host ? source.slice(host.length + 1) : undefined;
70
+ }
71
+
72
+ /**
73
+ * Every form a module source matches as: as written without its query, then
74
+ * its registry forms. A hostless registry source adds itself at each default
75
+ * registry; one at a default registry adds its hostless form and its form at
76
+ * the other default registry; `tfr:///x` adds `x` at each default registry
77
+ * and `tfr://<host>/x` adds `<host>/x`. Any other source (local, git, OCI,
78
+ * an archive URL) has the one form. A source through one of `proxies` adds
79
+ * the forms of the upstream source it stands for.
80
+ */
81
+ export function moduleSourceForms(source: string, options?: SourceFormOptions): string[] {
82
+ const out = plainModuleForms(source);
83
+ const via = moduleThroughProxy(source, options?.proxies);
84
+ if (via) for (const f of plainModuleForms(via.upstream)) if (!out.includes(f)) out.push(f);
85
+ return out;
86
+ }
87
+
88
+ function plainModuleForms(source: string): string[] {
89
+ const written = withoutQuery(source);
90
+ const out = [written];
91
+ const add = (...forms: string[]) => {
92
+ for (const f of forms) if (!out.includes(f)) out.push(f);
93
+ };
94
+ const tfr = /^tfr:\/\/([^/]*)\/(.+)$/.exec(written);
95
+ if (tfr) {
96
+ if (tfr[1]) add(`${tfr[1].toLowerCase()}/${tfr[2]}`);
97
+ else add(...DEFAULT_REGISTRIES.map((h) => `${h}/${tfr[2]}`));
98
+ if (!tfr[1]) add(tfr[2]!);
99
+ return out;
100
+ }
101
+ if (isHostlessModule(written)) {
102
+ add(...DEFAULT_REGISTRIES.map((h) => `${h}/${written}`));
103
+ return out;
104
+ }
105
+ const hostless = atDefaultRegistry(written);
106
+ if (hostless && isHostlessModule(hostless)) add(hostless, ...DEFAULT_REGISTRIES.map((h) => `${h}/${hostless}`));
107
+ return out;
108
+ }
109
+
110
+ /**
111
+ * The module a pin-free source names, written one way: a registry source
112
+ * with no host gets the public registry's (or the binary's), a host is
113
+ * lower-cased, and Terragrunt's `tfr:///ns/name/system` and
114
+ * `tfr://host/ns/name/system` become the plain registry form. Other sources
115
+ * (git, OCI, an archive URL) are kept as they are. Moved from
116
+ * ../pin/consumers.ts (chant#4599). A source through one of `proxies` is
117
+ * the upstream source it stands for.
118
+ */
119
+ export function normalizeModuleSource(module: string, options?: SourceFormArg): string {
120
+ const o = optionsOf(options);
121
+ const binary = o.binary ?? "terraform";
122
+ const via = moduleThroughProxy(module, o.proxies);
123
+ let s = (via ? via.upstream : module).trim();
124
+ const tfr = /^tfr:\/\/([^/]*)\/(.*)$/.exec(s);
125
+ if (tfr) s = `${tfr[1] || defaultRegistryHost(binary)}/${tfr[2]}`;
126
+ const [base, ...rest] = s.split("//");
127
+ const parts = base!.split("/");
128
+ const registryShaped = (n: number) => parts.length === n && parts.every((p) => p.length > 0) && !base!.includes(":");
129
+ if (registryShaped(3) && !/[.]/.test(parts[0]!)) return [`${defaultRegistryHost(binary)}/${base}`, ...rest].join("//");
130
+ const reg = parts.length === 4 ? parseRegistrySource(base!) : undefined;
131
+ if (reg) return [`${reg.host}/${parts.slice(1).join("/")}`, ...rest].join("//");
132
+ return s;
133
+ }
134
+
135
+ /** A provider address's parts. */
136
+ export interface ProviderAddress {
137
+ host: string;
138
+ namespace: string;
139
+ type: string;
140
+ /** `<host>/<namespace>/<type>`. */
141
+ address: string;
142
+ }
143
+
144
+ const PROVIDER_PART = /^[0-9a-z](?:[0-9a-z_-]{0,62}[0-9a-z])?$/;
145
+ const HOST = /^[a-z0-9.-]+(?::\d+)?$/;
146
+
147
+ /**
148
+ * A `required_providers` source in its full form, `<host>/<namespace>/<type>`,
149
+ * lower case, with the binary's default host when it names none and the
150
+ * `hashicorp` namespace when it names only a type. Undefined for a string
151
+ * that is not a provider address. A source through one of `proxies`
152
+ * (`<host>/<repo>__<ns>/<type>` on Artifactory) gives the upstream's address.
153
+ */
154
+ export function parseProviderAddress(source: string, options?: SourceFormArg): ProviderAddress | undefined {
155
+ const o = optionsOf(options);
156
+ const binary = o.binary ?? "terraform";
157
+ const via = providerThroughProxy(source, o.proxies);
158
+ const parts = (via ? via.upstream : source).trim().toLowerCase().split("/");
159
+ let host: string, namespace: string, type: string;
160
+ if (parts.length === 1) [host, namespace, type] = [defaultRegistryHost(binary), "hashicorp", parts[0]!];
161
+ else if (parts.length === 2) [host, namespace, type] = [defaultRegistryHost(binary), parts[0]!, parts[1]!];
162
+ else if (parts.length === 3) [host, namespace, type] = parts as [string, string, string];
163
+ else return undefined;
164
+ if (!HOST.test(host) || !PROVIDER_PART.test(namespace) || !PROVIDER_PART.test(type)) return undefined;
165
+ return { host, namespace, type, address: `${host}/${namespace}/${type}` };
166
+ }
167
+
168
+ /** {@link parseProviderAddress}'s full form, or undefined. */
169
+ export const providerAddress = (source: string, options?: SourceFormArg): string | undefined => parseProviderAddress(source, options)?.address;
170
+
171
+ /**
172
+ * The provider `init` installs for a local name that has no
173
+ * `required_providers` entry: `hashicorp/<name>`. Undefined for the binary's
174
+ * own `terraform` provider (terraform_data, terraform_remote_state), which is
175
+ * never downloaded.
176
+ */
177
+ export const impliedProvider = (local: string): string | undefined => (local === "terraform" ? undefined : `hashicorp/${local.toLowerCase()}`);
178
+
179
+ /**
180
+ * Every form a provider address matches as: in lower case as written, and,
181
+ * when written without a host or at a default registry, its hostless form
182
+ * and its form at each default registry. A source through one of `proxies`
183
+ * adds the forms of its upstream address.
184
+ */
185
+ export function providerAddressForms(source: string, options?: SourceFormOptions): string[] {
186
+ const out = plainProviderForms(source);
187
+ const via = providerThroughProxy(source, options?.proxies);
188
+ if (via) for (const f of plainProviderForms(via.upstream)) if (!out.includes(f)) out.push(f);
189
+ return out;
190
+ }
191
+
192
+ function plainProviderForms(source: string): string[] {
193
+ const a = source.trim().toLowerCase();
194
+ const out = [a];
195
+ const add = (...forms: string[]) => {
196
+ for (const f of forms) if (!out.includes(f)) out.push(f);
197
+ };
198
+ const hostless = a.split("/").length === 2 ? a : a.split("/").length === 1 && a ? `hashicorp/${a}` : atDefaultRegistry(a);
199
+ if (hostless && hostless.split("/").length === 2) add(hostless, ...DEFAULT_REGISTRIES.map((h) => `${h}/${hostless}`));
200
+ return out;
201
+ }
202
+
203
+ /**
204
+ * The provider addresses a `.terraform.lock.hcl` names, in the order
205
+ * written, as the lock file writes them (the full form). Read by text: the
206
+ * lock file is generated and every entry is a `provider "<address>" {` line.
207
+ */
208
+ export function lockedProviders(lockText: string): string[] {
209
+ const out: string[] = [];
210
+ for (const m of lockText.matchAll(/^\s*provider\s+"([^"]+)"\s*\{/gm)) {
211
+ const a = m[1]!.toLowerCase();
212
+ if (!out.includes(a)) out.push(a);
213
+ }
214
+ return out;
215
+ }
216
+
@@ -0,0 +1,248 @@
1
+ // chant#4605: the Artifactory module target against the in-memory Artifactory fake. No request
2
+ // leaves the process, and no credential appears in a log line, an error or a staged record.
3
+ import { describe, expect, it } from "vitest";
4
+ import { PublishTargetError } from "@intentius/chant/lifecycle/release-publish";
5
+ import { createRedactor } from "@intentius/chant/lifecycle/publish-credentials";
6
+ import { archiveContent, artifactoryBase, artifactoryDeployUrl, artifactoryModuleTarget, moduleZip, readModuleZipComment, readZip, type ModuleVersionContext } from "../index";
7
+ import { createFakeArtifactory, type FakeArtifactoryOptions } from "./fake-artifactory";
8
+ import { moduleTarball } from "./fake-http";
9
+
10
+ const HOST = "acme.jfrog.io";
11
+ const URL_ = `https://${HOST}/artifactory`;
12
+ const USER = "ci-deployer";
13
+ const TOKEN = "jfrog-access-0123456789abcdef";
14
+ const READER = "ci-reader";
15
+ const READER_TOKEN = "jfrog-reader-fedcba9876543210";
16
+ const ENV = { JFROG_USER: USER, JFROG_ACCESS_TOKEN: TOKEN };
17
+
18
+ const fake = (over: Partial<FakeArtifactoryOptions> = {}) =>
19
+ createFakeArtifactory({ host: HOST, repos: ["terraform-local"], users: { [USER]: TOKEN, [READER]: READER_TOKEN }, deployers: { "terraform-local": [USER] }, ...over });
20
+
21
+ const clock = () => {
22
+ let t = 0;
23
+ const sleeps: number[] = [];
24
+ return { now: () => t, sleep: async (ms: number) => void (sleeps.push(ms), (t += ms)), sleeps };
25
+ };
26
+
27
+ const archiveOf = (text: string) => moduleTarball({ "main.tf": `# ${text}\n`, "README.md": "# vpc\n" });
28
+
29
+ function context(env: NodeJS.ProcessEnv, over: Partial<ModuleVersionContext> = {}): ModuleVersionContext & { lines: string[] } {
30
+ const archive = over.archive ?? archiveOf("vpc 1.2.0");
31
+ const redactor = createRedactor(env, ["JFROG_ACCESS_TOKEN"]);
32
+ const lines: string[] = [];
33
+ return {
34
+ cwd: "/nowhere",
35
+ env,
36
+ redactor,
37
+ log: (line) => lines.push(redactor.redact(line)),
38
+ provenance: { oidc: false, ci: null, reason: "not in CI" },
39
+ tag: null,
40
+ address: { namespace: "acme", name: "vpc", system: "aws" },
41
+ version: "1.2.0",
42
+ revision: "3f2a9c1d0b7e3f2a9c1d0b7e3f2a9c1d0b7e3f2a",
43
+ content: archiveContent(archive),
44
+ archive,
45
+ ...over,
46
+ lines,
47
+ };
48
+ }
49
+
50
+ const target = (af: ReturnType<typeof fake>, opts = {}, c = clock()) =>
51
+ artifactoryModuleTarget({ url: URL_, repo: "terraform-local", ...opts }, { fetch: af.fetch, registryFetch: af.fetch, sleep: c.sleep, now: c.now });
52
+
53
+ const errorOf = async (p: Promise<unknown>): Promise<PublishTargetError> => {
54
+ const e = await p.then(
55
+ () => undefined,
56
+ (err: unknown) => err,
57
+ );
58
+ expect(e).toBeInstanceOf(PublishTargetError);
59
+ return e as PublishTargetError;
60
+ };
61
+
62
+ const noSecret = (text: string) => {
63
+ expect(text).not.toContain(TOKEN);
64
+ expect(text).not.toContain(Buffer.from(`${USER}:${TOKEN}`).toString("base64"));
65
+ };
66
+
67
+ describe("Artifactory module target (chant#4605)", () => {
68
+ it("deploys the zip of the attested archive with one PUT, basic credentials and application/zip", async () => {
69
+ const af = fake();
70
+ const ctx = context(ENV);
71
+ const t = target(af);
72
+ expect(await t.plan(ctx)).toEqual([{ name: "terraform-local__acme/vpc/aws 1.2.0", destination: `${HOST}/terraform-local__acme/vpc/aws`, digest: ctx.content }]);
73
+ const staged = await t.stage(ctx);
74
+ const puts = af.requests.filter((r) => r.method === "PUT");
75
+ expect(puts).toHaveLength(1);
76
+ expect(puts[0]!.url).toBe(`${URL_}/terraform-local/acme/vpc/aws/1.2.0.zip`);
77
+ expect(puts[0]!.headers.authorization).toBe(`Basic ${Buffer.from(`${USER}:${TOKEN}`).toString("base64")}`);
78
+ expect(puts[0]!.headers["content-type"]).toBe("application/zip");
79
+ expect(Buffer.compare(Buffer.from(puts[0]!.body!), moduleZip(ctx.archive, ctx.content, ctx.revision))).toBe(0);
80
+ const zip = readZip(puts[0]!.body!);
81
+ expect(zip.entries.map((e) => e.path).sort()).toEqual(["README.md", "main.tf"]);
82
+ expect(readModuleZipComment(zip.comment)?.content).toBe(ctx.content);
83
+ noSecret(JSON.stringify(staged));
84
+
85
+ const published = await t.reveal(ctx, staged);
86
+ expect(published.destination).toBe(`${HOST}/terraform-local__acme/vpc/aws`);
87
+ expect(await t.versions(ctx.address, ctx)).toEqual(["1.2.0"]);
88
+ expect(await t.held(ctx.address, "1.2.0", ctx)).toEqual({ content: ctx.content, revision: ctx.revision });
89
+ });
90
+
91
+ it("reads through the registry protocol at <host>/<repo>__<ns>/<name>/<system>", async () => {
92
+ const af = fake();
93
+ const ctx = context(ENV);
94
+ const t = target(af);
95
+ await t.reveal(ctx, await t.stage(ctx));
96
+ const reads = af.requests.filter((r) => r.method === "GET").map((r) => new URL(r.url).pathname);
97
+ expect(reads).toContain("/.well-known/terraform.json");
98
+ expect(reads).toContain("/artifactory/api/terraform/v1/modules/terraform-local__acme/vpc/aws/versions");
99
+ expect(reads).toContain("/artifactory/api/terraform/v1/modules/terraform-local__acme/vpc/aws/1.2.0/download");
100
+ });
101
+
102
+ it("sends TF_TOKEN_<host> on the protocol reads, apart from the deploy credentials", async () => {
103
+ const af = fake();
104
+ const ctx = context({ ...ENV, TF_TOKEN_acme_jfrog_io: READER_TOKEN });
105
+ const t = target(af);
106
+ await t.reveal(ctx, await t.stage(ctx));
107
+ const versionsRead = af.requests.find((r) => r.url.endsWith("/versions"))!;
108
+ expect(versionsRead.headers.authorization).toBe(`Bearer ${READER_TOKEN}`);
109
+ const put = af.requests.find((r) => r.method === "PUT")!;
110
+ expect(put.headers.authorization).toMatch(/^Basic /);
111
+ });
112
+
113
+ it("waits for Artifactory to index the deploy before the reveal", async () => {
114
+ const af = fake({ indexAfter: 2 });
115
+ const c = clock();
116
+ const ctx = context(ENV);
117
+ const t = target(af, { pollMs: 1000 }, c);
118
+ await t.reveal(ctx, await t.stage(ctx));
119
+ expect(c.sleeps).toEqual([1000, 1000]);
120
+ });
121
+
122
+ it("stops after the wait with the index fix when the version is never listed", async () => {
123
+ const af = fake({ indexAfter: 100 });
124
+ const ctx = context(ENV);
125
+ const t = target(af, { waitMs: 3000, pollMs: 1000 });
126
+ const e = await errorOf(t.reveal(ctx, await t.stage(ctx)));
127
+ expect(e.code).toBe("publish-reveal-missing");
128
+ expect(e.fix).toContain("Recalculate Index");
129
+ });
130
+
131
+ it("sends nothing for a version already deployed with the same content", async () => {
132
+ const af = fake();
133
+ const ctx = context(ENV);
134
+ af.seed("terraform-local", "acme", "vpc", "aws", "1.2.0", moduleZip(ctx.archive, ctx.content, ctx.revision));
135
+ const staged = await target(af).stage(ctx);
136
+ expect(staged.items[0]!.present).toBe(true);
137
+ expect(af.requests.filter((r) => r.method === "PUT")).toHaveLength(0);
138
+ expect(ctx.lines.join("\n")).toContain("nothing sent");
139
+ });
140
+
141
+ it("refuses a version already deployed with other content", async () => {
142
+ const af = fake();
143
+ const ctx = context(ENV);
144
+ const other = archiveOf("something else");
145
+ af.seed("terraform-local", "acme", "vpc", "aws", "1.2.0", moduleZip(other, archiveContent(other)));
146
+ const e = await errorOf(target(af).stage(ctx));
147
+ expect(e.code).toBe("publish-version-held");
148
+ expect(af.requests.filter((r) => r.method === "PUT")).toHaveLength(0);
149
+ });
150
+
151
+ it("refuses a zip deployed by hand, which names no attested content", async () => {
152
+ const af = fake();
153
+ const ctx = context(ENV);
154
+ af.seed("terraform-local", "acme", "vpc", "aws", "1.2.0", Buffer.from("PK\x05\x06" + "\0".repeat(18), "latin1"));
155
+ expect((await errorOf(target(af).stage(ctx))).code).toBe("publish-version-held");
156
+ });
157
+
158
+ it("judges a 409 after a race by what the repository then holds", async () => {
159
+ const af = fake();
160
+ const ctx = context(ENV);
161
+ // The held read answers 404; another run's deploy then lands before ours, which gets 409.
162
+ const real = af.fetch;
163
+ let raced = false;
164
+ const racing: typeof af.fetch = async (url, init) => {
165
+ if (!raced && init?.method === "PUT") {
166
+ raced = true;
167
+ af.seed("terraform-local", "acme", "vpc", "aws", "1.2.0", moduleZip(ctx.archive, ctx.content, ctx.revision));
168
+ }
169
+ return real(url, init);
170
+ };
171
+ const t2 = artifactoryModuleTarget({ url: URL_, repo: "terraform-local" }, { fetch: racing, registryFetch: racing });
172
+ const staged = await t2.stage(ctx);
173
+ expect(staged.items[0]!.present).toBe(true);
174
+ expect(af.requests.filter((r) => r.method === "PUT")).toHaveLength(1);
175
+ });
176
+
177
+ it("names the variables on a refused deploy, never their values", async () => {
178
+ const af = fake({ deployers: { "terraform-local": [] } });
179
+ const ctx = context(ENV);
180
+ const e = await errorOf(target(af).stage(ctx));
181
+ expect(e.code).toBe("publish-credentials-refused");
182
+ expect(e.message).toContain("JFROG_USER");
183
+ expect(e.message).toContain("JFROG_ACCESS_TOKEN");
184
+ noSecret(`${e.message} ${e.fix}`);
185
+ });
186
+
187
+ it("gives a 401 for an unknown token as refused credentials", async () => {
188
+ const af = fake();
189
+ const ctx = context({ JFROG_USER: USER, JFROG_ACCESS_TOKEN: "jfrog-wrong-token-000" });
190
+ const e = await errorOf(target(af).stage(ctx));
191
+ expect(e.code).toBe("publish-credentials-refused");
192
+ expect(e.message).not.toContain("jfrog-wrong-token-000");
193
+ });
194
+
195
+ it("refuses to plan without a user or a token", async () => {
196
+ const af = fake();
197
+ const e1 = await errorOf(target(af).plan(context({ JFROG_ACCESS_TOKEN: TOKEN })));
198
+ expect(e1.code).toBe("publish-credentials-missing");
199
+ expect(e1.message).toContain("JFROG_USER");
200
+ const e2 = await errorOf(target(af).plan(context({ JFROG_USER: USER })));
201
+ expect(e2.message).toContain("JFROG_ACCESS_TOKEN");
202
+ expect(await target(af, { user: USER }).plan(context({ JFROG_ACCESS_TOKEN: TOKEN }))).toHaveLength(1);
203
+ });
204
+
205
+ it("reads the token and user from the declared variables", async () => {
206
+ const af = fake();
207
+ const ctx = context({ DEPLOY_USER: USER, DEPLOY_TOKEN: TOKEN });
208
+ const staged = await target(af, { userEnv: "DEPLOY_USER", tokenEnv: "DEPLOY_TOKEN" }).stage(ctx);
209
+ expect(staged.items[0]!.present).toBeUndefined();
210
+ });
211
+
212
+ it("fails a 5xx with the status and no credential", async () => {
213
+ const af = fake();
214
+ const ctx = context(ENV);
215
+ const failing: typeof af.fetch = async (url, init) => (init?.method === "PUT" ? new Response("upstream down", { status: 503 }) : af.fetch(url, init));
216
+ const e = await errorOf(artifactoryModuleTarget({ url: URL_, repo: "terraform-local" }, { fetch: failing, registryFetch: failing }).stage(ctx));
217
+ expect(e.code).toBe("publish-upload-failed");
218
+ expect(e.message).toContain("503");
219
+ noSecret(e.message);
220
+ });
221
+
222
+ it("answers a 401 from failNext on the first read as refused credentials", async () => {
223
+ const af = fake();
224
+ af.failNext(401);
225
+ const e = await errorOf(target(af).stage(context(ENV)));
226
+ expect(e.code).toBe("publish-credentials-refused");
227
+ });
228
+
229
+ it("refuses an archive whose content is not the release's", async () => {
230
+ const af = fake();
231
+ const ctx = context(ENV, { content: archiveContent(archiveOf("other")) });
232
+ expect((await errorOf(target(af).plan(ctx))).code).toBe("publish-artifact-digest-mismatch");
233
+ });
234
+
235
+ it("builds the deploy URL and the base", () => {
236
+ expect(artifactoryBase("https://acme.jfrog.io")).toBe("https://acme.jfrog.io/artifactory");
237
+ expect(artifactoryBase("https://acme.jfrog.io/artifactory/")).toBe("https://acme.jfrog.io/artifactory");
238
+ expect(artifactoryDeployUrl("https://h/artifactory", "terraform-local", { namespace: "acme", name: "vpc", system: "aws" }, "1.0.0")).toBe("https://h/artifactory/terraform-local/acme/vpc/aws/1.0.0.zip");
239
+ });
240
+
241
+ it("masks the credentials in every log line", async () => {
242
+ const af = fake();
243
+ const ctx = context(ENV);
244
+ const t = target(af);
245
+ await t.reveal(ctx, await t.stage(ctx));
246
+ noSecret(ctx.lines.join("\n"));
247
+ });
248
+ });