@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.
- package/README.md +14 -10
- package/dist/__fixtures__/pr-loop/estate.component.d.ts +7 -0
- package/dist/__fixtures__/pr-loop/estate.component.d.ts.map +1 -0
- package/dist/__fixtures__/pr-loop/estate.component.js +7 -0
- package/dist/__fixtures__/report/generate.d.ts +26 -0
- package/dist/__fixtures__/report/generate.d.ts.map +1 -0
- package/dist/__fixtures__/report/generate.js +58 -0
- package/dist/applied-state.d.ts +27 -0
- package/dist/applied-state.d.ts.map +1 -0
- package/dist/applied-state.js +44 -0
- package/dist/backend-config.d.ts +53 -0
- package/dist/backend-config.d.ts.map +1 -0
- package/dist/backend-config.js +136 -0
- package/dist/behaviour/kinds.d.ts.map +1 -1
- package/dist/behaviour/kinds.js +4 -0
- package/dist/behaviour/predict.d.ts.map +1 -1
- package/dist/behaviour/predict.js +6 -2
- package/dist/behaviour/reference-catalog.d.ts +16 -5
- package/dist/behaviour/reference-catalog.d.ts.map +1 -1
- package/dist/behaviour/reference-catalog.js +16 -5
- package/dist/behaviour/request.d.ts.map +1 -1
- package/dist/behaviour/request.js +3 -2
- package/dist/changed-roots.d.ts +6 -4
- package/dist/changed-roots.d.ts.map +1 -1
- package/dist/changed-roots.js +14 -18
- package/dist/ci-setup.d.ts +11 -0
- package/dist/ci-setup.d.ts.map +1 -0
- package/dist/ci-setup.js +41 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +4 -3
- package/dist/components/capability-plugin.d.ts +105 -9
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability-plugin.js +357 -36
- package/dist/components/compose-group.d.ts +145 -0
- package/dist/components/compose-group.d.ts.map +1 -0
- package/dist/components/compose-group.js +407 -0
- package/dist/components/placeholder-preview.d.ts +78 -0
- package/dist/components/placeholder-preview.d.ts.map +1 -0
- package/dist/components/placeholder-preview.js +254 -0
- package/dist/components/release-inputs.d.ts +29 -0
- package/dist/components/release-inputs.d.ts.map +1 -0
- package/dist/components/release-inputs.js +57 -0
- package/dist/components/trace-facts.d.ts +16 -0
- package/dist/components/trace-facts.d.ts.map +1 -0
- package/dist/components/trace-facts.js +19 -0
- package/dist/compose/apply-events.d.ts +46 -0
- package/dist/compose/apply-events.d.ts.map +1 -0
- package/dist/compose/apply-events.js +77 -0
- package/dist/compose/estate.d.ts +194 -0
- package/dist/compose/estate.d.ts.map +1 -0
- package/dist/compose/estate.js +363 -0
- package/dist/compose/hcl.d.ts +99 -0
- package/dist/compose/hcl.d.ts.map +1 -0
- package/dist/compose/hcl.js +302 -0
- package/dist/compose/module-address.d.ts +24 -0
- package/dist/compose/module-address.d.ts.map +1 -0
- package/dist/compose/module-address.js +43 -0
- package/dist/compose/root.d.ts +127 -0
- package/dist/compose/root.d.ts.map +1 -0
- package/dist/compose/root.js +586 -0
- package/dist/compose/run.d.ts +183 -0
- package/dist/compose/run.d.ts.map +1 -0
- package/dist/compose/run.js +447 -0
- package/dist/compose/slice.d.ts +102 -0
- package/dist/compose/slice.d.ts.map +1 -0
- package/dist/compose/slice.js +249 -0
- package/dist/compose/state-address.d.ts +28 -0
- package/dist/compose/state-address.d.ts.map +1 -0
- package/dist/compose/state-address.js +154 -0
- package/dist/compose/state-merge.d.ts +52 -0
- package/dist/compose/state-merge.d.ts.map +1 -0
- package/dist/compose/state-merge.js +135 -0
- package/dist/compose.d.ts +179 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +482 -0
- package/dist/composites/terraform-apply-op.d.ts.map +1 -1
- package/dist/composites/terraform-apply-op.js +7 -3
- package/dist/cone.d.ts +53 -0
- package/dist/cone.d.ts.map +1 -0
- package/dist/cone.js +119 -0
- package/dist/config.d.ts +59 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +43 -9
- package/dist/cost-hold.d.ts +27 -0
- package/dist/cost-hold.d.ts.map +1 -0
- package/dist/cost-hold.js +21 -0
- package/dist/describe-resources.d.ts +74 -12
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/describe-resources.js +374 -66
- package/dist/drift-read.d.ts +33 -0
- package/dist/drift-read.d.ts.map +1 -0
- package/dist/drift-read.js +243 -0
- package/dist/drift-record.d.ts +147 -0
- package/dist/drift-record.d.ts.map +1 -0
- package/dist/drift-record.js +383 -0
- package/dist/drift-rows.d.ts +61 -0
- package/dist/drift-rows.d.ts.map +1 -0
- package/dist/drift-rows.js +108 -0
- package/dist/exit-codes.d.ts +55 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +48 -0
- package/dist/hcl/edges.d.ts.map +1 -1
- package/dist/hcl/edges.js +24 -1
- package/dist/hcl/files.d.ts +61 -0
- package/dist/hcl/files.d.ts.map +1 -0
- package/dist/hcl/files.js +221 -0
- package/dist/hcl/instances.d.ts +82 -0
- package/dist/hcl/instances.d.ts.map +1 -0
- package/dist/hcl/instances.js +390 -0
- package/dist/hcl/parse.d.ts +81 -9
- package/dist/hcl/parse.d.ts.map +1 -1
- package/dist/hcl/parse.js +114 -16
- package/dist/hcl/roots.d.ts.map +1 -1
- package/dist/hcl/roots.js +5 -0
- package/dist/hcl/suppressions.d.ts.map +1 -1
- package/dist/hcl/suppressions.js +8 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -2
- package/dist/integrity.json +8 -5
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-catalog.js +27 -3
- package/dist/lint/audit-lineage.d.ts +14 -2
- package/dist/lint/audit-lineage.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/index.js +4 -0
- package/dist/lint/post-synth/record-store.d.ts +54 -0
- package/dist/lint/post-synth/record-store.d.ts.map +1 -0
- package/dist/lint/post-synth/record-store.js +104 -0
- package/dist/lint/post-synth/tf001.d.ts +2 -2
- package/dist/lint/post-synth/tf001.js +2 -2
- package/dist/lint/post-synth/tf024.d.ts +35 -11
- package/dist/lint/post-synth/tf024.d.ts.map +1 -1
- package/dist/lint/post-synth/tf024.js +92 -20
- package/dist/lint/post-synth/tf046.d.ts +64 -0
- package/dist/lint/post-synth/tf046.d.ts.map +1 -0
- package/dist/lint/post-synth/tf046.js +175 -0
- package/dist/lint/post-synth/tf047.d.ts +36 -0
- package/dist/lint/post-synth/tf047.d.ts.map +1 -0
- package/dist/lint/post-synth/tf047.js +94 -0
- package/dist/live-plan-reasons.d.ts +21 -0
- package/dist/live-plan-reasons.d.ts.map +1 -0
- package/dist/live-plan-reasons.js +47 -0
- package/dist/lock-rows.d.ts +36 -0
- package/dist/lock-rows.d.ts.map +1 -0
- package/dist/lock-rows.js +60 -0
- package/dist/lock-timeout.d.ts +22 -0
- package/dist/lock-timeout.d.ts.map +1 -0
- package/dist/lock-timeout.js +32 -0
- package/dist/lsp/option-keys.d.ts.map +1 -1
- package/dist/lsp/option-keys.js +2 -0
- package/dist/manifest.json +1 -1
- package/dist/module-catalog/index.d.ts +69 -0
- package/dist/module-catalog/index.d.ts.map +1 -0
- package/dist/module-catalog/index.js +110 -0
- package/dist/module-catalog/refresh-cli.d.ts +4 -0
- package/dist/module-catalog/refresh-cli.d.ts.map +1 -0
- package/dist/module-catalog/refresh-cli.js +64 -0
- package/dist/module-catalog/static-tier.d.ts +6 -0
- package/dist/module-catalog/static-tier.d.ts.map +1 -0
- package/dist/module-catalog/static-tier.js +1650 -0
- package/dist/module-catalog/types.d.ts +65 -0
- package/dist/module-catalog/types.d.ts.map +1 -0
- package/dist/module-catalog/types.js +33 -0
- package/dist/okf/index.md +3 -1
- package/dist/okf/rules/TF024.md +2 -2
- package/dist/okf/rules/TF046.md +11 -0
- package/dist/okf/rules/TF047.md +11 -0
- package/dist/op/activities/index.d.ts +6 -2
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/index.js +5 -1
- package/dist/op/activities/live-detect.d.ts +4 -3
- package/dist/op/activities/live-detect.d.ts.map +1 -1
- package/dist/op/activities/live-detect.js +57 -32
- package/dist/op/activities/pin-rollout.d.ts +2 -0
- package/dist/op/activities/pin-rollout.d.ts.map +1 -1
- package/dist/op/activities/pin-rollout.js +1 -0
- package/dist/op/activities/terraform.d.ts +182 -5
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activities/terraform.js +224 -24
- package/dist/op/activity-contracts.d.ts +27 -0
- package/dist/op/activity-contracts.d.ts.map +1 -1
- package/dist/op/activity-contracts.js +19 -1
- package/dist/op/builders.d.ts +7 -1
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +9 -0
- package/dist/pin/consumers.d.ts +35 -0
- package/dist/pin/consumers.d.ts.map +1 -0
- package/dist/pin/consumers.js +70 -0
- package/dist/pin/edit.d.ts.map +1 -1
- package/dist/pin/edit.js +35 -0
- package/dist/pin/rollout.d.ts +2 -0
- package/dist/pin/rollout.d.ts.map +1 -1
- package/dist/pin/rollout.js +14 -11
- package/dist/plan-shape.d.ts +30 -0
- package/dist/plan-shape.d.ts.map +1 -0
- package/dist/plan-shape.js +125 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +37 -18
- package/dist/registry/address.d.ts +63 -0
- package/dist/registry/address.d.ts.map +1 -0
- package/dist/registry/address.js +65 -0
- package/dist/registry/catalog-read.d.ts +14 -0
- package/dist/registry/catalog-read.d.ts.map +1 -0
- package/dist/registry/catalog-read.js +58 -0
- package/dist/registry/deprecated.d.ts +37 -0
- package/dist/registry/deprecated.d.ts.map +1 -0
- package/dist/registry/deprecated.js +43 -0
- package/dist/registry/docs.d.ts +96 -0
- package/dist/registry/docs.d.ts.map +1 -0
- package/dist/registry/docs.js +210 -0
- package/dist/registry/edge/edge.d.ts +46 -0
- package/dist/registry/edge/edge.d.ts.map +1 -0
- package/dist/registry/edge/edge.js +194 -0
- package/dist/registry/edge/index.d.ts +11 -0
- package/dist/registry/edge/index.d.ts.map +1 -0
- package/dist/registry/edge/index.js +10 -0
- package/dist/registry/edge/lambda-edge.d.ts +76 -0
- package/dist/registry/edge/lambda-edge.d.ts.map +1 -0
- package/dist/registry/edge/lambda-edge.js +95 -0
- package/dist/registry/edge/sigv4-web.d.ts +33 -0
- package/dist/registry/edge/sigv4-web.d.ts.map +1 -0
- package/dist/registry/edge/sigv4-web.js +56 -0
- package/dist/registry/edge/token-source.d.ts +73 -0
- package/dist/registry/edge/token-source.d.ts.map +1 -0
- package/dist/registry/edge/token-source.js +115 -0
- package/dist/registry/edge/worker.d.ts +45 -0
- package/dist/registry/edge/worker.d.ts.map +1 -0
- package/dist/registry/edge/worker.js +26 -0
- package/dist/registry/handler.d.ts +25 -0
- package/dist/registry/handler.d.ts.map +1 -0
- package/dist/registry/handler.js +75 -0
- package/dist/registry/headers-file.d.ts +21 -0
- package/dist/registry/headers-file.d.ts.map +1 -0
- package/dist/registry/headers-file.js +57 -0
- package/dist/registry/index.d.ts +31 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +30 -0
- package/dist/registry/mirror-paths.d.ts +9 -0
- package/dist/registry/mirror-paths.d.ts.map +1 -0
- package/dist/registry/mirror-paths.js +8 -0
- package/dist/registry/mirror.d.ts +130 -0
- package/dist/registry/mirror.d.ts.map +1 -0
- package/dist/registry/mirror.js +319 -0
- package/dist/registry/proxies.d.ts +73 -0
- package/dist/registry/proxies.d.ts.map +1 -0
- package/dist/registry/proxies.js +111 -0
- package/dist/registry/reader.d.ts +57 -0
- package/dist/registry/reader.d.ts.map +1 -0
- package/dist/registry/reader.js +198 -0
- package/dist/registry/semver.d.ts +16 -0
- package/dist/registry/semver.d.ts.map +1 -0
- package/dist/registry/semver.js +24 -0
- package/dist/registry/serve.d.ts +24 -0
- package/dist/registry/serve.d.ts.map +1 -0
- package/dist/registry/serve.js +64 -0
- package/dist/registry/source-forms.d.ts +79 -0
- package/dist/registry/source-forms.d.ts.map +1 -0
- package/dist/registry/source-forms.js +202 -0
- package/dist/registry/targets/artifactory.d.ts +28 -0
- package/dist/registry/targets/artifactory.d.ts.map +1 -0
- package/dist/registry/targets/artifactory.js +206 -0
- package/dist/registry/targets/fake-artifactory.d.ts +30 -0
- package/dist/registry/targets/fake-artifactory.d.ts.map +1 -0
- package/dist/registry/targets/fake-artifactory.js +142 -0
- package/dist/registry/targets/fake-gitlab.d.ts +39 -0
- package/dist/registry/targets/fake-gitlab.d.ts.map +1 -0
- package/dist/registry/targets/fake-gitlab.js +136 -0
- package/dist/registry/targets/fake-hcp.d.ts +50 -0
- package/dist/registry/targets/fake-hcp.d.ts.map +1 -0
- package/dist/registry/targets/fake-hcp.js +202 -0
- package/dist/registry/targets/fake-http.d.ts +31 -0
- package/dist/registry/targets/fake-http.d.ts.map +1 -0
- package/dist/registry/targets/fake-http.js +87 -0
- package/dist/registry/targets/fake-nexus.d.ts +27 -0
- package/dist/registry/targets/fake-nexus.d.ts.map +1 -0
- package/dist/registry/targets/fake-nexus.js +137 -0
- package/dist/registry/targets/gitlab.d.ts +53 -0
- package/dist/registry/targets/gitlab.d.ts.map +1 -0
- package/dist/registry/targets/gitlab.js +187 -0
- package/dist/registry/targets/hcp.d.ts +29 -0
- package/dist/registry/targets/hcp.d.ts.map +1 -0
- package/dist/registry/targets/hcp.js +310 -0
- package/dist/registry/targets/hosted.d.ts +62 -0
- package/dist/registry/targets/hosted.d.ts.map +1 -0
- package/dist/registry/targets/hosted.js +74 -0
- package/dist/registry/targets/index.d.ts +18 -0
- package/dist/registry/targets/index.d.ts.map +1 -0
- package/dist/registry/targets/index.js +17 -0
- package/dist/registry/targets/module-target.d.ts +68 -0
- package/dist/registry/targets/module-target.d.ts.map +1 -0
- package/dist/registry/targets/module-target.js +63 -0
- package/dist/registry/targets/nexus.d.ts +43 -0
- package/dist/registry/targets/nexus.d.ts.map +1 -0
- package/dist/registry/targets/nexus.js +250 -0
- package/dist/registry/targets/zip.d.ts +55 -0
- package/dist/registry/targets/zip.d.ts.map +1 -0
- package/dist/registry/targets/zip.js +278 -0
- package/dist/registry/writer.d.ts +101 -0
- package/dist/registry/writer.d.ts.map +1 -0
- package/dist/registry/writer.js +109 -0
- package/dist/report.d.ts +15 -6
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +28 -55
- package/dist/rules/record-store.ts +122 -0
- package/dist/rules/tf001.ts +2 -2
- package/dist/rules/tf024.ts +105 -22
- package/dist/rules/tf046.ts +204 -0
- package/dist/rules/tf047.ts +114 -0
- package/dist/skills/chant-terraform.md +1 -1
- package/dist/state/azure-blob.d.ts +9 -0
- package/dist/state/azure-blob.d.ts.map +1 -0
- package/dist/state/azure-blob.js +8 -0
- package/dist/state/backend.d.ts +158 -0
- package/dist/state/backend.d.ts.map +1 -0
- package/dist/state/backend.js +406 -0
- package/dist/state/exec.d.ts +39 -0
- package/dist/state/exec.d.ts.map +1 -0
- package/dist/state/exec.js +80 -0
- package/dist/state/gcs.d.ts +8 -0
- package/dist/state/gcs.d.ts.map +1 -0
- package/dist/state/gcs.js +7 -0
- package/dist/state/index.d.ts +15 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +14 -0
- package/dist/state/locks.d.ts +105 -0
- package/dist/state/locks.d.ts.map +1 -0
- package/dist/state/locks.js +130 -0
- package/dist/state/object-store.d.ts +8 -0
- package/dist/state/object-store.d.ts.map +1 -0
- package/dist/state/object-store.js +7 -0
- package/dist/state/root-states.d.ts +131 -0
- package/dist/state/root-states.d.ts.map +1 -0
- package/dist/state/root-states.js +241 -0
- package/dist/state/s3.d.ts +21 -0
- package/dist/state/s3.d.ts.map +1 -0
- package/dist/state/s3.js +19 -0
- package/dist/state/store.d.ts +51 -0
- package/dist/state/store.d.ts.map +1 -0
- package/dist/state/store.js +185 -0
- package/dist/state-ops.d.ts +255 -0
- package/dist/state-ops.d.ts.map +1 -0
- package/dist/state-ops.js +305 -0
- package/dist/state-reads.d.ts +57 -0
- package/dist/state-reads.d.ts.map +1 -0
- package/dist/state-reads.js +104 -0
- package/dist/terragrunt/affected.d.ts.map +1 -1
- package/dist/terragrunt/affected.js +2 -1
- package/dist/used-sources.d.ts +54 -0
- package/dist/used-sources.d.ts.map +1 -0
- package/dist/used-sources.js +157 -0
- package/package.json +24 -4
- package/src/__fixtures__/ACCEPTANCE.md +69 -7
- package/src/__fixtures__/compose/apply-failed-in-app.jsonl +12 -0
- package/src/__fixtures__/compose/state-app.json +66 -0
- package/src/__fixtures__/compose/state-edge.json +40 -0
- package/src/__fixtures__/compose/state-net.json +58 -0
- package/src/__fixtures__/compose/three-app.plan.json +184 -0
- package/src/__fixtures__/compose/three-composed.plan.json +509 -0
- package/src/__fixtures__/compose/three-edge.plan.json +144 -0
- package/src/__fixtures__/compose/three-net.plan.json +174 -0
- package/src/__fixtures__/compose/unknown-composed.plan.json +228 -0
- package/src/__fixtures__/compose/working-after-apply.json +145 -0
- package/src/__fixtures__/config-blocks/app/main.tf +61 -0
- package/src/__fixtures__/config-blocks/app/modules/network/main.tf +4 -0
- package/src/__fixtures__/config-blocks/app-json/main.tf.json +39 -0
- package/src/__fixtures__/config-files/shadow/main.tf +3 -0
- package/src/__fixtures__/config-files/shadow/main.tofu +3 -0
- package/src/__fixtures__/config-files/shadow/other.tf +3 -0
- package/src/__fixtures__/config-files/tf/main.tf +11 -0
- package/src/__fixtures__/config-files/tf-json/main.tf.json +18 -0
- package/src/__fixtures__/config-files/tofu/main.tofu +11 -0
- package/src/__fixtures__/config-files/tofu-json/main.tofu.json +18 -0
- package/src/__fixtures__/config-files/tofu-only/main.tofu +3 -0
- package/src/__fixtures__/drift-record/README.md +16 -0
- package/src/__fixtures__/drift-record/live-plan-sections.json +27 -0
- package/src/__fixtures__/drift-rows/live-root-plan.json +70 -0
- package/src/__fixtures__/drift-rows/refresh-plan.json +61 -0
- package/src/__fixtures__/instance-forms/app/main.tf +110 -0
- package/src/__fixtures__/instance-forms/app/modules/svc/main.tf +3 -0
- package/src/__fixtures__/instance-vars/app/main.tf +52 -0
- package/src/__fixtures__/instance-vars/app/modules/app/main.tf +12 -0
- package/src/__fixtures__/instance-vars/app/modules/edge/main.tf +3 -0
- package/src/__fixtures__/instance-vars/app/terraform.tfvars +1 -0
- package/src/__fixtures__/live-module-keyed/live-graph.json +259 -0
- package/src/__fixtures__/live-module-keyed/live-plan.json +87 -0
- package/src/__fixtures__/live-module-keyed/root/estate.chdf.hcl +1 -0
- package/src/__fixtures__/live-module-keyed/root/main.tf +21 -0
- package/src/__fixtures__/live-module-keyed/root/modules/app/main.tf +17 -0
- package/src/__fixtures__/live-module-keyed/root/modules/edge/main.tf +7 -0
- package/src/__fixtures__/live-module-keyed/root/versions.tf +30 -0
- package/src/__fixtures__/live-module-keyed/show-state.json +146 -0
- package/src/__fixtures__/pr-loop/README.md +50 -0
- package/src/__fixtures__/pr-loop/chant.config.json +13 -0
- package/src/__fixtures__/pr-loop/estate.component.ts +10 -0
- package/src/__fixtures__/pr-loop/roots/a/main.tf +11 -0
- package/src/__fixtures__/pr-loop/roots/app/main.tf +19 -0
- package/src/__fixtures__/pr-loop/roots/b/main.tf +7 -0
- package/src/__fixtures__/pr-loop/roots/dns/main.tf +3 -0
- package/src/__fixtures__/pr-loop/roots/net/main.tf +7 -0
- package/src/__fixtures__/report/applied.show.json +51 -0
- package/src/__fixtures__/report/generate.ts +63 -0
- package/src/__fixtures__/report/plan.show.json +200 -0
- package/src/__fixtures__/report/tf-apply/change-set.json +170 -0
- package/src/__fixtures__/report/tf-apply/report.json +338 -0
- package/src/__fixtures__/report/tf-plan/change-set.json +170 -0
- package/src/__fixtures__/report/tf-plan/report.json +320 -0
- package/src/__fixtures__/three-cloud-azure.graph.json +290 -0
- package/src/__fixtures__/used-sources/root/extra.tofu +1 -0
- package/src/__fixtures__/used-sources/root/main.tf +31 -0
- package/src/__fixtures__/used-sources/root/modules/net/main.tf +14 -0
- package/src/__fixtures__/used-sources/root/modules/net/versions.tf.json +7 -0
- package/src/__fixtures__/used-sources/unit/terragrunt.hcl +3 -0
- package/src/applied-state.ts +52 -0
- package/src/backend-config.test.ts +105 -0
- package/src/backend-config.ts +133 -0
- package/src/behaviour/kinds.ts +4 -0
- package/src/behaviour/predict.ts +5 -2
- package/src/behaviour/reference-catalog.plugin.test.ts +65 -0
- package/src/behaviour/reference-catalog.ts +16 -5
- package/src/behaviour/request.ts +3 -2
- package/src/change-set-instances.test.ts +98 -0
- package/src/changed-roots.ts +14 -16
- package/src/ci-setup.ts +42 -0
- package/src/commands.ts +4 -3
- package/src/components/capability-plugin.test.ts +198 -19
- package/src/components/capability-plugin.ts +383 -35
- package/src/components/change-set-graph.e2e.test.ts +11 -5
- package/src/components/compose-group.test.ts +486 -0
- package/src/components/compose-group.ts +489 -0
- package/src/components/demo-composed-roots.e2e.test.ts +440 -0
- package/src/components/member-outputs.test.ts +76 -0
- package/src/components/placeholder-preview.test.ts +147 -0
- package/src/components/placeholder-preview.ts +257 -0
- package/src/components/pr-loop-compose.tofu.e2e.test.ts +363 -0
- package/src/components/pr-loop.tofu.test.ts +113 -57
- package/src/components/release-inputs.ts +69 -0
- package/src/components/trace-facts.test.ts +21 -0
- package/src/components/trace-facts.ts +27 -0
- package/src/compose/apply-events.test.ts +62 -0
- package/src/compose/apply-events.ts +100 -0
- package/src/compose/estate.choudoufu.e2e.test.ts +112 -0
- package/src/compose/estate.test.ts +421 -0
- package/src/compose/estate.ts +443 -0
- package/src/compose/hcl.ts +311 -0
- package/src/compose/module-address.ts +49 -0
- package/src/compose/root.test.ts +416 -0
- package/src/compose/root.ts +592 -0
- package/src/compose/run.test.ts +440 -0
- package/src/compose/run.tofu.test.ts +176 -0
- package/src/compose/run.ts +527 -0
- package/src/compose/slice.test.ts +184 -0
- package/src/compose/slice.ts +290 -0
- package/src/compose/state-address.ts +157 -0
- package/src/compose/state-merge.test.ts +131 -0
- package/src/compose/state-merge.ts +148 -0
- package/src/compose.test.ts +248 -0
- package/src/compose.ts +627 -0
- package/src/composites/terraform-apply-op.test.ts +9 -0
- package/src/composites/terraform-apply-op.ts +7 -3
- package/src/cone.ts +138 -0
- package/src/config-file-listers.test.ts +265 -0
- package/src/config.ts +44 -9
- package/src/cost-hold.ts +29 -0
- package/src/describe-resources.engine-facts.test.ts +411 -0
- package/src/describe-resources.live-module-keyed.test.ts +290 -0
- package/src/describe-resources.ts +416 -70
- package/src/drift-read.ts +284 -0
- package/src/drift-record.test.ts +223 -0
- package/src/drift-record.ts +455 -0
- package/src/drift-rows.test.ts +135 -0
- package/src/drift-rows.ts +137 -0
- package/src/exit-codes.test.ts +23 -0
- package/src/exit-codes.ts +59 -0
- package/src/hcl/config-blocks.test.ts +174 -0
- package/src/hcl/edges.ts +21 -1
- package/src/hcl/files.ts +234 -0
- package/src/hcl/instance-forms.test.ts +94 -0
- package/src/hcl/instances.test.ts +137 -0
- package/src/hcl/instances.ts +422 -0
- package/src/hcl/parse.test.ts +139 -1
- package/src/hcl/parse.ts +149 -16
- package/src/hcl/roots.ts +6 -0
- package/src/hcl/suppressions.ts +8 -2
- package/src/index.ts +79 -0
- package/src/lint/audit-catalog.ts +29 -3
- package/src/lint/audit-lineage.ts +14 -2
- package/src/lint/post-synth/fixtures/TF024/live-with-record-store/main.tf +18 -0
- package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/estate.chdf.hcl +1 -0
- package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/main.tf +10 -0
- package/src/lint/post-synth/fixtures/TF046/azurerm-no-resource-group/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF046/backend-config-only/main.tf +14 -0
- package/src/lint/post-synth/fixtures/TF046/derived-s3/backend.tf +7 -0
- package/src/lint/post-synth/fixtures/TF046/derived-s3/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF046/explicit-local/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF046/explicit-s3-beside-local-backend/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF046/implied-local/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF046/sidecar-implied/estate.chdf.hcl +1 -0
- package/src/lint/post-synth/fixtures/TF046/sidecar-implied/main.tf +5 -0
- package/src/lint/post-synth/fixtures/TF047/clear/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/refused/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/unknown-module/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/unknown-version/main.tf +12 -0
- package/src/lint/post-synth/index.ts +4 -0
- package/src/lint/post-synth/record-store.ts +122 -0
- package/src/lint/post-synth/tf001.ts +2 -2
- package/src/lint/post-synth/tf024.test.ts +26 -5
- package/src/lint/post-synth/tf024.ts +105 -22
- package/src/lint/post-synth/tf046.test.ts +137 -0
- package/src/lint/post-synth/tf046.ts +204 -0
- package/src/lint/post-synth/tf047.test.ts +56 -0
- package/src/lint/post-synth/tf047.ts +114 -0
- package/src/live-plan-reasons.ts +54 -0
- package/src/lock-rows.test.ts +70 -0
- package/src/lock-rows.ts +82 -0
- package/src/lock-timeout.ts +34 -0
- package/src/lsp/completions.test.ts +1 -1
- package/src/lsp/option-keys.ts +2 -0
- package/src/module-catalog/index.test.ts +93 -0
- package/src/module-catalog/index.ts +159 -0
- package/src/module-catalog/refresh-cli.ts +68 -0
- package/src/module-catalog/static-tier.ts +1659 -0
- package/src/module-catalog/types.ts +88 -0
- package/src/op/activities/choudoufu-plan-set-op.test.ts +111 -0
- package/src/op/activities/index.ts +9 -0
- package/src/op/activities/live-detect.ts +55 -26
- package/src/op/activities/pin-rollout.ts +3 -0
- package/src/op/activities/registry.test.ts +2 -1
- package/src/op/activities/terraform.test.ts +55 -0
- package/src/op/activities/terraform.ts +312 -27
- package/src/op/activity-contracts.test.ts +1 -0
- package/src/op/activity-contracts.ts +24 -1
- package/src/op/builders.ts +14 -0
- package/src/op-wave-step.test.ts +109 -0
- package/src/pin/__fixtures__/consumers/live/dev/main.tf +4 -0
- package/src/pin/__fixtures__/consumers/live/prod/main.tf +21 -0
- package/src/pin/__fixtures__/consumers/units/vpc/terragrunt.hcl +7 -0
- package/src/pin/bundle.test.ts +2 -0
- package/src/pin/consumers.test.ts +57 -0
- package/src/pin/consumers.ts +95 -0
- package/src/pin/edit.ts +30 -0
- package/src/pin/rollout.ts +16 -11
- package/src/plan-shape.test.ts +148 -0
- package/src/plan-shape.ts +128 -0
- package/src/plugin.ts +39 -19
- package/src/registry/__fixtures__/docs/documented/.terraform-docs.yml +3 -0
- package/src/registry/__fixtures__/docs/documented/README.md +1 -0
- package/src/registry/__fixtures__/docs/documented/main.tf +8 -0
- package/src/registry/__fixtures__/docs/json/main.tf.json +14 -0
- package/src/registry/__fixtures__/docs/vpc/README.md +3 -0
- package/src/registry/__fixtures__/docs/vpc/examples/complete/main.tf +5 -0
- package/src/registry/__fixtures__/docs/vpc/examples/simple/README.md +1 -0
- package/src/registry/__fixtures__/docs/vpc/examples/simple/main.tf +4 -0
- package/src/registry/__fixtures__/docs/vpc/main.tf +14 -0
- package/src/registry/__fixtures__/docs/vpc/outputs.tf +9 -0
- package/src/registry/__fixtures__/docs/vpc/variables.tf +16 -0
- package/src/registry/__fixtures__/proxied-root/main.tf +12 -0
- package/src/registry/address.ts +100 -0
- package/src/registry/catalog-read.ts +56 -0
- package/src/registry/deprecated.test.ts +139 -0
- package/src/registry/deprecated.ts +76 -0
- package/src/registry/docs.test.ts +140 -0
- package/src/registry/docs.ts +299 -0
- package/src/registry/edge/bundle.test.ts +50 -0
- package/src/registry/edge/edge.test.ts +176 -0
- package/src/registry/edge/edge.ts +211 -0
- package/src/registry/edge/index.ts +10 -0
- package/src/registry/edge/lambda-edge.ts +127 -0
- package/src/registry/edge/sigv4-web.ts +79 -0
- package/src/registry/edge/token-source.test.ts +258 -0
- package/src/registry/edge/token-source.ts +169 -0
- package/src/registry/edge/worker.ts +61 -0
- package/src/registry/handler.ts +89 -0
- package/src/registry/headers-file.ts +61 -0
- package/src/registry/index.ts +31 -0
- package/src/registry/mirror-paths.ts +10 -0
- package/src/registry/mirror.test.ts +198 -0
- package/src/registry/mirror.ts +395 -0
- package/src/registry/proxies.test.ts +153 -0
- package/src/registry/proxies.ts +132 -0
- package/src/registry/reader.ts +225 -0
- package/src/registry/registry.test.ts +262 -0
- package/src/registry/semver.ts +34 -0
- package/src/registry/serve.ts +82 -0
- package/src/registry/source-forms.test.ts +87 -0
- package/src/registry/source-forms.ts +216 -0
- package/src/registry/targets/artifactory.test.ts +248 -0
- package/src/registry/targets/artifactory.ts +257 -0
- package/src/registry/targets/fake-artifactory.ts +158 -0
- package/src/registry/targets/fake-gitlab.ts +161 -0
- package/src/registry/targets/fake-hcp.ts +233 -0
- package/src/registry/targets/fake-http.ts +97 -0
- package/src/registry/targets/fake-nexus.ts +152 -0
- package/src/registry/targets/gitlab.test.ts +208 -0
- package/src/registry/targets/gitlab.ts +223 -0
- package/src/registry/targets/hcp.test.ts +230 -0
- package/src/registry/targets/hcp.ts +360 -0
- package/src/registry/targets/hosted.ts +117 -0
- package/src/registry/targets/index.ts +17 -0
- package/src/registry/targets/module-target.ts +131 -0
- package/src/registry/targets/nexus.test.ts +220 -0
- package/src/registry/targets/nexus.ts +314 -0
- package/src/registry/targets/zip.test.ts +65 -0
- package/src/registry/targets/zip.ts +287 -0
- package/src/registry/writer.ts +155 -0
- package/src/report.test.ts +82 -1
- package/src/report.ts +31 -54
- package/src/signal-only-root.test.ts +72 -0
- package/src/skills/chant-terraform.md +1 -1
- package/src/state/azure-blob.ts +8 -0
- package/src/state/backend-verdict.test.ts +187 -0
- package/src/state/backend.test.ts +127 -0
- package/src/state/backend.ts +488 -0
- package/src/state/exec.ts +111 -0
- package/src/state/gcs.ts +7 -0
- package/src/state/index.ts +15 -0
- package/src/state/locks.test.ts +84 -0
- package/src/state/locks.ts +201 -0
- package/src/state/object-store.ts +7 -0
- package/src/state/root-states-floci.e2e.test.ts +66 -0
- package/src/state/root-states.test.ts +462 -0
- package/src/state/root-states.ts +309 -0
- package/src/state/s3.ts +21 -0
- package/src/state/store.ts +225 -0
- package/src/state-ops.test.ts +182 -0
- package/src/state-ops.ts +462 -0
- package/src/state-reads.ts +116 -0
- package/src/terragrunt/affected.ts +2 -1
- package/src/used-sources.test.ts +75 -0
- package/src/used-sources.ts +204 -0
- package/src/workspace-kinds.test.ts +4 -17
- package/workspace-kinds.json +1 -24
- package/dist/workspace-member.d.ts +0 -30
- package/dist/workspace-member.d.ts.map +0 -1
- package/dist/workspace-member.js +0 -91
- package/schemas/pulumi.schema.json +0 -34
- package/src/testdata/pulumi-member/Pulumi.yaml +0 -2
- package/src/testdata/pulumi-member/stack-export.json +0 -53
- package/src/testdata/pulumi-unexported/Pulumi.yaml +0 -2
- package/src/workspace-member.test.ts +0 -89
- package/src/workspace-member.ts +0 -126
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy source forms (chant#4606): module and provider sources written
|
|
3
|
+
* through an Artifactory remote repository or a Nexus proxy host block
|
|
4
|
+
* compare equal to the plain upstream source, and the registry reader reads
|
|
5
|
+
* a plain source through a fake proxy. No request leaves the process.
|
|
6
|
+
*/
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { describe, expect, test } from "vitest";
|
|
9
|
+
import {
|
|
10
|
+
artifactoryProxy,
|
|
11
|
+
moduleSourceForms,
|
|
12
|
+
moduleThroughProxy,
|
|
13
|
+
nexusProxy,
|
|
14
|
+
normalizeModuleSource,
|
|
15
|
+
providerAddress,
|
|
16
|
+
providerAddressForms,
|
|
17
|
+
providerThroughProxy,
|
|
18
|
+
proxyRoute,
|
|
19
|
+
registryLocation,
|
|
20
|
+
registryVersions,
|
|
21
|
+
} from "./index";
|
|
22
|
+
import { usedSources } from "../used-sources";
|
|
23
|
+
import { createFakeArtifactory } from "./targets/fake-artifactory";
|
|
24
|
+
import { createFakeNexus } from "./targets/fake-nexus";
|
|
25
|
+
import { moduleTarball } from "./targets/fake-http";
|
|
26
|
+
import { moduleZip } from "./targets/zip";
|
|
27
|
+
import { archiveContent } from "./targets/module-target";
|
|
28
|
+
|
|
29
|
+
const AF = artifactoryProxy("acme.jfrog.io", "tf-remote");
|
|
30
|
+
const NX = nexusProxy({ url: "https://nexus.example.com", repo: "tf-proxy" });
|
|
31
|
+
const NX_LABEL = nexusProxy({ url: "https://nexus.example.com", repo: "tf-group", host: "nexus-tf" });
|
|
32
|
+
const proxies = [AF, NX_LABEL];
|
|
33
|
+
|
|
34
|
+
describe("module forms through a proxy", () => {
|
|
35
|
+
test("an Artifactory remote source stands for the upstream module", () => {
|
|
36
|
+
const src = "acme.jfrog.io/tf-remote__terraform-aws-modules/vpc/aws?ref=x";
|
|
37
|
+
expect(moduleThroughProxy(src, proxies)).toEqual({ proxy: AF, upstream: "registry.terraform.io/terraform-aws-modules/vpc/aws" });
|
|
38
|
+
expect(moduleSourceForms(src, { proxies })).toEqual([
|
|
39
|
+
"acme.jfrog.io/tf-remote__terraform-aws-modules/vpc/aws",
|
|
40
|
+
"registry.terraform.io/terraform-aws-modules/vpc/aws",
|
|
41
|
+
"terraform-aws-modules/vpc/aws",
|
|
42
|
+
"registry.opentofu.org/terraform-aws-modules/vpc/aws",
|
|
43
|
+
]);
|
|
44
|
+
expect(normalizeModuleSource(src.split("?")[0]!, { proxies })).toBe(normalizeModuleSource("terraform-aws-modules/vpc/aws"));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("keeps a subdirectory and takes the repository key in any case", () => {
|
|
48
|
+
expect(moduleThroughProxy("ACME.jfrog.io/TF-Remote__acme/vpc/aws//modules/sub", proxies)?.upstream).toBe("registry.terraform.io/acme/vpc/aws//modules/sub");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("a namespace without the repository's prefix, or another repository, is no proxy source", () => {
|
|
52
|
+
expect(moduleThroughProxy("acme.jfrog.io/terraform-local__acme/vpc/aws", proxies)).toBeUndefined();
|
|
53
|
+
expect(moduleThroughProxy("acme.jfrog.io/acme/vpc/aws", proxies)).toBeUndefined();
|
|
54
|
+
expect(moduleSourceForms("acme.jfrog.io/acme/vpc/aws", { proxies })).toEqual(["acme.jfrog.io/acme/vpc/aws"]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("a Nexus host block label stands for its upstream, and one named for the upstream is the plain source", () => {
|
|
58
|
+
expect(moduleThroughProxy("nexus-tf/acme/vpc/aws", proxies)?.upstream).toBe("registry.terraform.io/acme/vpc/aws");
|
|
59
|
+
expect(normalizeModuleSource("nexus-tf/acme/vpc/aws", { proxies })).toBe("registry.terraform.io/acme/vpc/aws");
|
|
60
|
+
expect(moduleThroughProxy("registry.terraform.io/acme/vpc/aws", [NX])?.upstream).toBe("registry.terraform.io/acme/vpc/aws");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("a proxy for another upstream maps to that upstream", () => {
|
|
64
|
+
const tofu = artifactoryProxy("acme.jfrog.io", "tofu-remote", "registry.opentofu.org");
|
|
65
|
+
expect(normalizeModuleSource("acme.jfrog.io/tofu-remote__acme/vpc/aws", { proxies: [tofu] })).toBe("registry.opentofu.org/acme/vpc/aws");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("without proxies nothing changes", () => {
|
|
69
|
+
expect(moduleSourceForms("acme.jfrog.io/tf-remote__acme/vpc/aws")).toEqual(["acme.jfrog.io/tf-remote__acme/vpc/aws"]);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("provider forms through a proxy", () => {
|
|
74
|
+
test("an Artifactory remote provider stands for the upstream provider", () => {
|
|
75
|
+
expect(providerThroughProxy("acme.jfrog.io/tf-remote__hashicorp/aws", proxies)?.upstream).toBe("registry.terraform.io/hashicorp/aws");
|
|
76
|
+
expect(providerAddress("acme.jfrog.io/tf-remote__hashicorp/AWS", { proxies })).toBe("registry.terraform.io/hashicorp/aws");
|
|
77
|
+
expect(providerAddress("acme.jfrog.io/tf-remote__hashicorp/aws", { binary: "tofu", proxies })).toBe("registry.terraform.io/hashicorp/aws");
|
|
78
|
+
expect(providerAddressForms("acme.jfrog.io/tf-remote__hashicorp/aws", { proxies })).toEqual([
|
|
79
|
+
"acme.jfrog.io/tf-remote__hashicorp/aws",
|
|
80
|
+
"registry.terraform.io/hashicorp/aws",
|
|
81
|
+
"hashicorp/aws",
|
|
82
|
+
"registry.opentofu.org/hashicorp/aws",
|
|
83
|
+
]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("a Nexus label provider stands for the upstream provider", () => {
|
|
87
|
+
expect(providerAddress("nexus-tf/hashicorp/aws", { proxies })).toBe("registry.terraform.io/hashicorp/aws");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("an address through no proxy is read as before", () => {
|
|
91
|
+
expect(providerAddress("acme.jfrog.io/acme/thing", { proxies })).toBe("acme.jfrog.io/acme/thing");
|
|
92
|
+
expect(providerAddress("hashicorp/aws", { proxies })).toBe("registry.terraform.io/hashicorp/aws");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("proxy routes", () => {
|
|
97
|
+
test("an upstream module goes to the proxy's host and namespace", () => {
|
|
98
|
+
expect(proxyRoute({ host: "registry.terraform.io", namespace: "acme" }, [AF])).toEqual({ proxy: AF, host: "acme.jfrog.io", namespace: "tf-remote__acme" });
|
|
99
|
+
expect(proxyRoute({ host: "registry.terraform.io", namespace: "acme" }, [NX])).toEqual({ proxy: NX, host: "registry.terraform.io", namespace: "acme", modules: "https://nexus.example.com/repository/tf-proxy/v1/modules/" });
|
|
100
|
+
expect(proxyRoute({ host: "registry.acme.dev", namespace: "acme" }, [AF])).toBeUndefined();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("reads through a fake proxy", () => {
|
|
105
|
+
const archive = moduleTarball({ "main.tf": "# vpc\n" });
|
|
106
|
+
const zip = moduleZip(archive, archiveContent(archive));
|
|
107
|
+
|
|
108
|
+
test("a plain source reads its versions and location through an Artifactory remote repository", async () => {
|
|
109
|
+
const af = createFakeArtifactory({ host: "acme.jfrog.io", repos: ["tf-remote"], users: { reader: "jfrog-read-0123456789" } });
|
|
110
|
+
af.seed("tf-remote", "terraform-aws-modules", "vpc", "aws", "5.1.0", zip);
|
|
111
|
+
const o = { proxies: [AF], fetch: af.fetch, token: "jfrog-read-0123456789" };
|
|
112
|
+
expect(await registryVersions("terraform-aws-modules/vpc/aws", o)).toEqual(["5.1.0"]);
|
|
113
|
+
expect(await registryVersions("registry.terraform.io/terraform-aws-modules/vpc/aws", o)).toEqual(["5.1.0"]);
|
|
114
|
+
expect(await registryLocation("terraform-aws-modules/vpc/aws", "5.1.0", o)).toBe("https://acme.jfrog.io/artifactory/tf-remote/terraform-aws-modules/vpc/aws/5.1.0.zip");
|
|
115
|
+
const paths = af.requests.map((r) => new URL(r.url).pathname);
|
|
116
|
+
expect(paths).toContain("/artifactory/api/terraform/v1/modules/tf-remote__terraform-aws-modules/vpc/aws/versions");
|
|
117
|
+
expect(af.requests.every((r) => new URL(r.url).host === "acme.jfrog.io")).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("a plain source reads through a Nexus proxy repository's host block, with no discovery", async () => {
|
|
121
|
+
const nx = createFakeNexus({ host: "nexus.example.com", repos: ["tf-proxy"], users: { reader: "nexus-read-0123456789" }, bearer: false });
|
|
122
|
+
nx.seed("tf-proxy", "terraform-aws-modules", "vpc", "aws", "5.1.0", "vpc-5.1.0.zip", zip);
|
|
123
|
+
const basic = `Basic ${Buffer.from("reader:nexus-read-0123456789").toString("base64")}`;
|
|
124
|
+
const o = { proxies: [NX], fetch: nx.fetch, token: false as const, authorization: basic };
|
|
125
|
+
expect(await registryVersions("terraform-aws-modules/vpc/aws", o)).toEqual(["5.1.0"]);
|
|
126
|
+
expect(await registryLocation("registry.terraform.io/terraform-aws-modules/vpc/aws", "5.1.0", o)).toBe(
|
|
127
|
+
"https://nexus.example.com/repository/tf-proxy/v1/modules/terraform-aws-modules/vpc/aws/5.1.0/vpc-5.1.0.zip",
|
|
128
|
+
);
|
|
129
|
+
expect(nx.requests.map((r) => new URL(r.url).pathname)).not.toContain("/.well-known/terraform.json");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("a source written through a Nexus label reads through it", async () => {
|
|
133
|
+
const nx = createFakeNexus({ host: "nexus.example.com", repos: ["tf-group"], users: { reader: "nexus-read-0123456789" }, bearer: false });
|
|
134
|
+
nx.seed("tf-group", "acme", "vpc", "aws", "1.0.0", "vpc-1.0.0.zip", zip);
|
|
135
|
+
const basic = `Basic ${Buffer.from("reader:nexus-read-0123456789").toString("base64")}`;
|
|
136
|
+
expect(await registryVersions("nexus-tf/acme/vpc/aws", { proxies: [NX_LABEL], fetch: nx.fetch, token: false, authorization: basic })).toEqual(["1.0.0"]);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("a hostless source without proxies is refused as before", async () => {
|
|
140
|
+
await expect(registryVersions("acme/vpc/aws", { fetch: async () => new Response(null, { status: 500 }) })).rejects.toThrow(/not a registry source/);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe("usedSources through a proxy", () => {
|
|
145
|
+
test("a call through an Artifactory remote repository gets the upstream's normal form", async () => {
|
|
146
|
+
const root = join(import.meta.dirname, "__fixtures__", "proxied-root");
|
|
147
|
+
const { used } = await usedSources(root, { proxies });
|
|
148
|
+
const vpc = used.find((u) => u.block === "module.vpc")!;
|
|
149
|
+
expect(vpc.source).toBe("acme.jfrog.io/tf-remote__terraform-aws-modules/vpc/aws");
|
|
150
|
+
expect(vpc.normal).toBe("registry.terraform.io/terraform-aws-modules/vpc/aws");
|
|
151
|
+
expect(used.find((u) => u.block === "required_providers.aws")!.normal).toBe("registry.terraform.io/hashicorp/aws");
|
|
152
|
+
});
|
|
153
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry proxies (chant#4606, terragucci#1290 bundle C): an Artifactory
|
|
3
|
+
* remote or virtual repository, or a Nexus proxy or group repository,
|
|
4
|
+
* standing in front of an upstream registry, so a source written through
|
|
5
|
+
* one and the plain upstream source compare equal, and reads go through the
|
|
6
|
+
* proxy. The shapes are the ones terragucci#1299's research found:
|
|
7
|
+
*
|
|
8
|
+
* Artifactory <host>/<repo>__<ns>/<name>/<system> module, the repository key joined to the namespace by `__`
|
|
9
|
+
* <host>/<repo>__<ns>/<type> provider, the same joiner
|
|
10
|
+
* Nexus <label>/<ns>/<name>/<system> behind a CLI `host "<label>"` block whose services
|
|
11
|
+
* point at the proxy repository; the label is usually
|
|
12
|
+
* the upstream itself (`host "registry.terraform.io"`)
|
|
13
|
+
*
|
|
14
|
+
* An entry names the host sources through the proxy write, the Artifactory
|
|
15
|
+
* repository key when there is one, and the upstream it stands for
|
|
16
|
+
* (registry.terraform.io unless set). A Nexus entry carries the repository's
|
|
17
|
+
* modules.v1 URL, which reads use in place of the label's discovery, as the
|
|
18
|
+
* CLI's host block does. No Node imports, so an edge function can carry it.
|
|
19
|
+
*/
|
|
20
|
+
import { parseRegistrySource } from "./address";
|
|
21
|
+
|
|
22
|
+
/** The upstream a proxy stands for when an entry names none. */
|
|
23
|
+
export const DEFAULT_PROXY_UPSTREAM = "registry.terraform.io";
|
|
24
|
+
|
|
25
|
+
/** One registry proxy. */
|
|
26
|
+
export interface RegistryProxy {
|
|
27
|
+
/** The host sources through the proxy name: Artifactory's host, or the label of a Nexus host block. */
|
|
28
|
+
host: string;
|
|
29
|
+
/** Artifactory: the remote or virtual repository's key, joined to the namespace with `__`. Absent for Nexus. */
|
|
30
|
+
repo?: string;
|
|
31
|
+
/** The registry host the proxy stands for. Default registry.terraform.io. */
|
|
32
|
+
upstream?: string;
|
|
33
|
+
/**
|
|
34
|
+
* The modules.v1 URL a CLI host block gives the label (a Nexus proxy or
|
|
35
|
+
* group repository's `<url>/repository/<repo>/v1/modules/`), read in
|
|
36
|
+
* place of the label's service discovery.
|
|
37
|
+
*/
|
|
38
|
+
modules?: string;
|
|
39
|
+
/** The providers.v1 URL of the same host block, when reads of providers go through it. */
|
|
40
|
+
providers?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The `__` an Artifactory source joins its repository key and namespace with. */
|
|
44
|
+
export const ARTIFACTORY_JOINER = "__";
|
|
45
|
+
|
|
46
|
+
/** A proxy entry for an Artifactory remote or virtual Terraform repository. */
|
|
47
|
+
export const artifactoryProxy = (host: string, repo: string, upstream?: string): RegistryProxy => ({ host: host.toLowerCase(), repo, ...(upstream ? { upstream: upstream.toLowerCase() } : {}) });
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A proxy entry for a Nexus proxy or group Terraform repository at `url`.
|
|
51
|
+
* The label defaults to the upstream, as Sonatype's docs write the host
|
|
52
|
+
* block (`host "registry.terraform.io"`), so plain sources go through it.
|
|
53
|
+
*/
|
|
54
|
+
export function nexusProxy(o: { url: string; repo: string; upstream?: string; host?: string }): RegistryProxy {
|
|
55
|
+
const base = o.url.replace(/\/+$/, "");
|
|
56
|
+
const upstream = (o.upstream ?? DEFAULT_PROXY_UPSTREAM).toLowerCase();
|
|
57
|
+
const repo = encodeURIComponent(o.repo);
|
|
58
|
+
return { host: (o.host ?? upstream).toLowerCase(), upstream, modules: `${base}/repository/${repo}/v1/modules/`, providers: `${base}/repository/${repo}/v1/providers/` };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const upstreamOf = (p: RegistryProxy): string => (p.upstream ?? DEFAULT_PROXY_UPSTREAM).toLowerCase();
|
|
62
|
+
const sameHost = (a: string, b: string) => a.toLowerCase() === b.toLowerCase();
|
|
63
|
+
|
|
64
|
+
/** The bare labels the proxies use as hosts, so a source starting with one parses as a registry source. */
|
|
65
|
+
export const proxyLabels = (proxies: readonly RegistryProxy[] = []): string[] => proxies.map((p) => p.host);
|
|
66
|
+
|
|
67
|
+
/** A namespace through an Artifactory proxy without its `<repo>__`, or undefined when it does not carry that repository. */
|
|
68
|
+
function unjoin(p: RegistryProxy, namespace: string): string | undefined {
|
|
69
|
+
if (!p.repo) return namespace;
|
|
70
|
+
const prefix = `${p.repo}${ARTIFACTORY_JOINER}`;
|
|
71
|
+
return namespace.toLowerCase().startsWith(prefix.toLowerCase()) && namespace.length > prefix.length ? namespace.slice(prefix.length) : undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The upstream registry source a module source written through a proxy
|
|
76
|
+
* stands for, `<upstream>/<ns>/<name>/<system>` with any `//subdir` kept and
|
|
77
|
+
* the query dropped, and the proxy it went through. Undefined for a source
|
|
78
|
+
* through none of them. A Nexus label that is the upstream itself gives the
|
|
79
|
+
* source back at the upstream.
|
|
80
|
+
*/
|
|
81
|
+
export function moduleThroughProxy(source: string, proxies: readonly RegistryProxy[] = []): { proxy: RegistryProxy; upstream: string } | undefined {
|
|
82
|
+
if (proxies.length === 0) return undefined;
|
|
83
|
+
const written = source.trim().split("?")[0]!;
|
|
84
|
+
const [base, ...sub] = written.split("//");
|
|
85
|
+
const s = parseRegistrySource(base!, proxyLabels(proxies));
|
|
86
|
+
if (!s) return undefined;
|
|
87
|
+
for (const p of proxies) {
|
|
88
|
+
if (!sameHost(p.host, s.host)) continue;
|
|
89
|
+
const ns = unjoin(p, s.namespace);
|
|
90
|
+
if (ns === undefined) continue;
|
|
91
|
+
const upstream = [`${upstreamOf(p)}/${ns}/${s.name}/${s.system}`, ...sub].join("//");
|
|
92
|
+
return { proxy: p, upstream };
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The upstream provider address a provider source written through a proxy
|
|
99
|
+
* stands for, `<upstream>/<ns>/<type>` in lower case. Undefined for a
|
|
100
|
+
* source through none of them.
|
|
101
|
+
*/
|
|
102
|
+
export function providerThroughProxy(source: string, proxies: readonly RegistryProxy[] = []): { proxy: RegistryProxy; upstream: string } | undefined {
|
|
103
|
+
const parts = source.trim().toLowerCase().split("/");
|
|
104
|
+
if (parts.length !== 3) return undefined;
|
|
105
|
+
const [host, namespace, type] = parts as [string, string, string];
|
|
106
|
+
for (const p of proxies) {
|
|
107
|
+
if (!sameHost(p.host, host)) continue;
|
|
108
|
+
const ns = unjoin(p, namespace);
|
|
109
|
+
if (ns === undefined || !ns || !type) continue;
|
|
110
|
+
return { proxy: p, upstream: `${upstreamOf(p)}/${ns}/${type}` };
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Where a read of a module at `host/<ns>/<name>/<system>` goes when a proxy
|
|
117
|
+
* stands for `host`: the host and namespace the proxy names it by, and the
|
|
118
|
+
* modules.v1 URL to read in place of discovery (a Nexus host block's).
|
|
119
|
+
* Undefined when no proxy stands for `host`, or the module already names a
|
|
120
|
+
* proxy's host.
|
|
121
|
+
*/
|
|
122
|
+
export function proxyRoute(
|
|
123
|
+
s: { host: string; namespace: string },
|
|
124
|
+
proxies: readonly RegistryProxy[] = [],
|
|
125
|
+
): { proxy: RegistryProxy; host: string; namespace: string; modules?: string } | undefined {
|
|
126
|
+
// A source written through a proxy already reads from its host; a Nexus label is resolved by its modules URL.
|
|
127
|
+
const named = proxies.find((p) => sameHost(p.host, s.host));
|
|
128
|
+
if (named) return named.modules ? { proxy: named, host: named.host, namespace: s.namespace, modules: named.modules } : undefined;
|
|
129
|
+
const p = proxies.find((q) => sameHost(upstreamOf(q), s.host));
|
|
130
|
+
if (!p) return undefined;
|
|
131
|
+
return { proxy: p, host: p.host, namespace: p.repo ? `${p.repo}${ARTIFACTORY_JOINER}${s.namespace}` : s.namespace, ...(p.modules ? { modules: p.modules } : {}) };
|
|
132
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a module registry the way the CLIs do: service discovery, the
|
|
3
|
+
* versions list, a version's download location, and a tarball's bytes.
|
|
4
|
+
* Lifted from terragucci's `publish/registry.ts` (18f6431595) for chant#4565.
|
|
5
|
+
*
|
|
6
|
+
* `registryLocation` reads the download answer in one of three ways:
|
|
7
|
+
* `terraform` takes only the `X-Terraform-Get` header (200 or 204), `tofu`
|
|
8
|
+
* takes the JSON body's `location` on a 200 and the header otherwise, and the
|
|
9
|
+
* default takes either, header first. A test that wants to know whether
|
|
10
|
+
* Terraform can install from a registry asks with `reads: "terraform"`.
|
|
11
|
+
*
|
|
12
|
+
* Every request to the registry's host carries the token the CLIs would send
|
|
13
|
+
* it (chant#4563): `TF_TOKEN_<host>`, else the CLI config's or
|
|
14
|
+
* credentials.tfrc.json's `credentials` block, found by core's
|
|
15
|
+
* `terraform/credentials` reader. The token goes in the `Authorization`
|
|
16
|
+
* header and nowhere else; an error names the variable to set, never a value.
|
|
17
|
+
*
|
|
18
|
+
* A host with no service discovery of its own, such as a Nexus repository
|
|
19
|
+
* (chant#4605), is read through `services`: the modules.v1 URL per host, as
|
|
20
|
+
* a CLI's `host` block gives it, held in memory so no CLI config file is
|
|
21
|
+
* written. Such a host may be a bare name with no dot, as a `host` block's
|
|
22
|
+
* label may be.
|
|
23
|
+
*
|
|
24
|
+
* With `proxies` (chant#4606), a module whose host a proxy stands for is
|
|
25
|
+
* read through the proxy: an Artifactory remote repository at
|
|
26
|
+
* `<proxy host>/<repo>__<ns>/<name>/<system>`, a Nexus proxy or group
|
|
27
|
+
* repository through its host block's modules.v1 URL. A source with no host
|
|
28
|
+
* is read at registry.terraform.io, so through the proxy that stands for it.
|
|
29
|
+
*/
|
|
30
|
+
import { gunzipSync } from "node:zlib";
|
|
31
|
+
import { credentialsHost, terraformToken, tokenVariable, type CredentialsOptions } from "@intentius/chant/terraform/credentials";
|
|
32
|
+
import { DISCOVERY, parseRegistrySource, type RegistrySource } from "./address";
|
|
33
|
+
import { DEFAULT_PROXY_UPSTREAM, proxyLabels, proxyRoute, type RegistryProxy } from "./proxies";
|
|
34
|
+
import { DOWNLOAD_HEADER, readVersions } from "./writer";
|
|
35
|
+
|
|
36
|
+
/** The fetch the reader calls: the global fetch's shape. */
|
|
37
|
+
export type RegistryFetch = (url: string, init?: RequestInit) => Promise<Response>;
|
|
38
|
+
|
|
39
|
+
const globalFetch: RegistryFetch = (url, init) => fetch(url, init);
|
|
40
|
+
|
|
41
|
+
export class RegistryError extends Error {
|
|
42
|
+
constructor(message: string) {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = "RegistryError";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** How a download answer is read. */
|
|
49
|
+
export type DownloadReader = "terraform" | "tofu" | "either";
|
|
50
|
+
|
|
51
|
+
export interface RegistryReadOptions {
|
|
52
|
+
fetch?: RegistryFetch;
|
|
53
|
+
/** The URL scheme service discovery is fetched over. Default https, as both CLIs use. */
|
|
54
|
+
scheme?: "https" | "http";
|
|
55
|
+
/**
|
|
56
|
+
* The bearer token sent to the registry's host. Default: the one the CLIs
|
|
57
|
+
* would send (`TF_TOKEN_<host>`, then the credentials files). `false` sends none.
|
|
58
|
+
*/
|
|
59
|
+
token?: string | false;
|
|
60
|
+
/** Where the default token lookup reads (a test passes its own env and files). */
|
|
61
|
+
credentials?: CredentialsOptions;
|
|
62
|
+
/**
|
|
63
|
+
* The modules.v1 URL per registry host, read in place of the host's
|
|
64
|
+
* `/.well-known/terraform.json`, as a CLI's `host "<name>" { services = {...} }`
|
|
65
|
+
* block does. A host named here is taken as a registry host even without a dot.
|
|
66
|
+
*/
|
|
67
|
+
services?: Record<string, string>;
|
|
68
|
+
/**
|
|
69
|
+
* A whole `Authorization` header value sent on every request to the
|
|
70
|
+
* registry, in place of the bearer token, such as Nexus's basic
|
|
71
|
+
* credentials before its bearer form (3.95). Never printed.
|
|
72
|
+
*/
|
|
73
|
+
authorization?: string;
|
|
74
|
+
/** Registry proxies in front of upstream hosts; a read of a module at an upstream goes through its proxy. */
|
|
75
|
+
proxies?: readonly RegistryProxy[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** The token a read sends to a host, by the options. */
|
|
79
|
+
export const readToken = (host: string, o: RegistryReadOptions): string | undefined => (o.token === false ? undefined : (o.token ?? terraformToken(host, o.credentials)));
|
|
80
|
+
|
|
81
|
+
const httpGet = async (fetchFn: RegistryFetch, url: string, token?: string, authorization?: string): Promise<Response> => {
|
|
82
|
+
const auth = authorization ?? (token ? `Bearer ${token}` : undefined);
|
|
83
|
+
try {
|
|
84
|
+
return await fetchFn(url, { method: "GET", redirect: "follow", ...(auth ? { headers: { Authorization: auth } } : {}) });
|
|
85
|
+
} catch (e) {
|
|
86
|
+
const cause = (e as { cause?: { message?: string } }).cause?.message;
|
|
87
|
+
throw new RegistryError(`${url}: ${(e as Error).message}${cause ? ` (${cause})` : ""}`);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** The error for a status that is not OK, saying where a token goes when the registry refused one. */
|
|
92
|
+
const answered = (url: string, status: number, host: string, token: string | undefined): RegistryError => {
|
|
93
|
+
if (status !== 401 && status !== 403) return new RegistryError(`${url} answered ${status}`);
|
|
94
|
+
const variable = tokenVariable(host);
|
|
95
|
+
const where = variable ? `${variable} or a credentials "${credentialsHost(host)}" block` : `a credentials "${credentialsHost(host) ?? host}" block in credentials.tfrc.json`;
|
|
96
|
+
return new RegistryError(token ? `${url} answered ${status}: the registry refused the token for ${host}` : `${url} answered ${status}: the registry wants a token; set ${where}`);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const optionsOf = (o: RegistryFetch | RegistryReadOptions | undefined): RegistryReadOptions => (typeof o === "function" ? { fetch: o } : (o ?? {}));
|
|
100
|
+
|
|
101
|
+
/** The modules.v1 URL `services` names for a host, matched without case. */
|
|
102
|
+
const serviceFor = (host: string, o: RegistryReadOptions): string | undefined => {
|
|
103
|
+
if (!o.services) return undefined;
|
|
104
|
+
const h = host.toLowerCase();
|
|
105
|
+
for (const [k, v] of Object.entries(o.services)) if (k.toLowerCase() === h) return v;
|
|
106
|
+
return undefined;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/** A source split by the hosts the options know as well as by the usual host shape. */
|
|
110
|
+
const sourceOf = (source: string, o: RegistryReadOptions) => parseRegistrySource(source, [...Object.keys(o.services ?? {}), ...proxyLabels(o.proxies)]);
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Where a read of `source` goes: the source as written, or, when a proxy
|
|
114
|
+
* stands for its host, the proxy's host and namespace, with the options
|
|
115
|
+
* carrying the proxy's modules.v1 URL when it has one. A source with no host
|
|
116
|
+
* is taken at registry.terraform.io only when proxies are given, so a read
|
|
117
|
+
* without them refuses it as before.
|
|
118
|
+
*/
|
|
119
|
+
function readTarget(source: string, o: RegistryReadOptions): { s: RegistrySource; o: RegistryReadOptions } | undefined {
|
|
120
|
+
let s = sourceOf(source, o);
|
|
121
|
+
if (!s && o.proxies?.length) {
|
|
122
|
+
const base = source.split("?")[0]!.split("//")[0]!;
|
|
123
|
+
const parts = base.split("/");
|
|
124
|
+
if (parts.length === 3 && parts.every((p) => /^[0-9A-Za-z][0-9A-Za-z_-]*$/.test(p))) s = { host: DEFAULT_PROXY_UPSTREAM, namespace: parts[0]!, name: parts[1]!, system: parts[2]! };
|
|
125
|
+
}
|
|
126
|
+
if (!s) return undefined;
|
|
127
|
+
const r = proxyRoute(s, o.proxies);
|
|
128
|
+
if (!r) return { s, o };
|
|
129
|
+
return { s: { ...s, host: r.host, namespace: r.namespace }, o: r.modules ? { ...o, services: { ...o.services, [r.host]: r.modules } } : o };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** The modules.v1 base a host's service discovery names, ending in `/`. */
|
|
133
|
+
export async function modulesBase(host: string, options?: RegistryFetch | RegistryReadOptions): Promise<string> {
|
|
134
|
+
const o = optionsOf(options);
|
|
135
|
+
const mapped = serviceFor(host, o);
|
|
136
|
+
if (mapped !== undefined) return mapped.endsWith("/") ? mapped : `${mapped}/`;
|
|
137
|
+
const disco = `${o.scheme ?? "https"}://${host}/${DISCOVERY}`;
|
|
138
|
+
const token = readToken(host, o);
|
|
139
|
+
const res = await httpGet(o.fetch ?? globalFetch, disco, token, o.authorization);
|
|
140
|
+
if (!res.ok) throw answered(disco, res.status, host, token ?? o.authorization);
|
|
141
|
+
let base: unknown;
|
|
142
|
+
try {
|
|
143
|
+
base = ((await res.json()) as Record<string, unknown>)["modules.v1"];
|
|
144
|
+
} catch {
|
|
145
|
+
throw new RegistryError(`${disco} is not JSON`);
|
|
146
|
+
}
|
|
147
|
+
if (typeof base !== "string") throw new RegistryError(`${disco} names no modules.v1`);
|
|
148
|
+
const url = new URL(base, disco).toString();
|
|
149
|
+
return url.endsWith("/") ? url : `${url}/`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Every version a registry lists for a module; none when it has no versions file. */
|
|
153
|
+
export async function registryVersions(source: string, options?: RegistryFetch | RegistryReadOptions): Promise<string[]> {
|
|
154
|
+
const t = readTarget(source, optionsOf(options));
|
|
155
|
+
if (!t) throw new RegistryError(`${source} is not a registry source`);
|
|
156
|
+
const { s, o } = t;
|
|
157
|
+
const token = readToken(s.host, o);
|
|
158
|
+
const url = `${await modulesBase(s.host, { ...o, token: token ?? false })}${s.namespace}/${s.name}/${s.system}/versions`;
|
|
159
|
+
const res = await httpGet(o.fetch ?? globalFetch, url, token, o.authorization);
|
|
160
|
+
if (res.status === 404) return [];
|
|
161
|
+
if (!res.ok) throw answered(url, res.status, s.host, token ?? o.authorization);
|
|
162
|
+
return readVersions(await res.text());
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface RegistryLocationOptions extends RegistryReadOptions {
|
|
166
|
+
/** Which CLI's reading to follow. Default `either`. */
|
|
167
|
+
reads?: DownloadReader;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** The location a download answer gives, read as `reads` says, or undefined when it gives none that way. */
|
|
171
|
+
export async function locationFromAnswer(res: Response, reads: DownloadReader = "either"): Promise<string | undefined> {
|
|
172
|
+
const header = res.headers.get(DOWNLOAD_HEADER) || undefined;
|
|
173
|
+
if (reads === "terraform") return res.status === 200 || res.status === 204 ? header : undefined;
|
|
174
|
+
let body: string | undefined;
|
|
175
|
+
if (res.status === 200) {
|
|
176
|
+
try {
|
|
177
|
+
const doc = (await res.json()) as { location?: unknown };
|
|
178
|
+
if (typeof doc.location === "string" && doc.location) body = doc.location;
|
|
179
|
+
} catch {
|
|
180
|
+
// OpenTofu fails on a 200 whose body is not JSON, before it looks at the header.
|
|
181
|
+
if (reads === "tofu") return undefined;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return reads === "tofu" ? (body ?? header) : (header ?? body);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Where a registry says one version is, resolved against the download URL when it is relative. */
|
|
188
|
+
export async function registryLocation(source: string, version: string, options?: RegistryFetch | RegistryLocationOptions): Promise<string> {
|
|
189
|
+
const t = readTarget(source, optionsOf(options));
|
|
190
|
+
if (!t) throw new RegistryError(`${source} is not a registry source`);
|
|
191
|
+
const s = t.s;
|
|
192
|
+
const o = t.o as RegistryLocationOptions;
|
|
193
|
+
const token = readToken(s.host, o);
|
|
194
|
+
const url = `${await modulesBase(s.host, { ...o, token: token ?? false })}${s.namespace}/${s.name}/${s.system}/${version}/download`;
|
|
195
|
+
const res = await httpGet(o.fetch ?? globalFetch, url, token, o.authorization);
|
|
196
|
+
if (res.status === 404) throw new RegistryError(`${s.host}/${s.namespace}/${s.name}/${s.system} has no version ${version}`);
|
|
197
|
+
if (!res.ok) throw answered(url, res.status, s.host, token ?? o.authorization);
|
|
198
|
+
const reads = o.reads ?? "either";
|
|
199
|
+
let location = await locationFromAnswer(res, reads);
|
|
200
|
+
if (!location) {
|
|
201
|
+
throw new RegistryError(reads === "terraform" ? `${url} sends no ${DOWNLOAD_HEADER} header, and Terraform reads only the header` : `${url} names no location`);
|
|
202
|
+
}
|
|
203
|
+
if (/^(\.{0,2}\/)/.test(location)) location = new URL(location, url).toString();
|
|
204
|
+
return location;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The module archive a tarball location holds, uncompressed: the bytes a
|
|
209
|
+
* release's digest is of. The host's token goes along only when the location
|
|
210
|
+
* has no query string; a signed URL carries its own grant, and a bucket
|
|
211
|
+
* refuses a request that has both a signature and an `Authorization` header.
|
|
212
|
+
*/
|
|
213
|
+
export async function fetchTarball(location: string, options?: RegistryFetch | RegistryReadOptions): Promise<Buffer> {
|
|
214
|
+
const o = optionsOf(options);
|
|
215
|
+
const u = new URL(location);
|
|
216
|
+
const token = u.search || (u.protocol !== "https:" && u.protocol !== "http:") ? undefined : readToken(u.host, o);
|
|
217
|
+
const res = await httpGet(o.fetch ?? globalFetch, location, token);
|
|
218
|
+
if (!res.ok) throw answered(location, res.status, u.host, token);
|
|
219
|
+
const body = Buffer.from(await res.arrayBuffer());
|
|
220
|
+
try {
|
|
221
|
+
return gunzipSync(body);
|
|
222
|
+
} catch {
|
|
223
|
+
throw new RegistryError(`${location} is not a gzip archive`);
|
|
224
|
+
}
|
|
225
|
+
}
|