@intentius/chant-lexicon-terraform 0.124.54 → 0.124.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- 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/components/capability-plugin.d.ts +40 -6
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability-plugin.js +174 -30
- package/dist/components/compose-group.d.ts +1 -1
- package/dist/components/compose-group.d.ts.map +1 -1
- package/dist/components/compose-group.js +1 -1
- 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/cone.d.ts +53 -0
- package/dist/cone.d.ts.map +1 -0
- package/dist/cone.js +119 -0
- 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/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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/integrity.json +7 -4
- 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/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/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/terraform.d.ts +29 -0
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activities/terraform.js +19 -4
- 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/plugin.d.ts.map +1 -1
- package/dist/plugin.js +5 -0
- 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/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/state/azure-blob.d.ts +8 -67
- package/dist/state/azure-blob.d.ts.map +1 -1
- package/dist/state/azure-blob.js +6 -199
- package/dist/state/gcs.d.ts +7 -61
- package/dist/state/gcs.d.ts.map +1 -1
- package/dist/state/gcs.js +5 -222
- package/dist/state/object-store.d.ts +4 -33
- package/dist/state/object-store.d.ts.map +1 -1
- package/dist/state/object-store.js +4 -17
- package/dist/state/s3.d.ts +11 -58
- package/dist/state/s3.d.ts.map +1 -1
- package/dist/state/s3.js +10 -161
- package/dist/state/store.js +2 -2
- 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/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 +19 -3
- package/src/__fixtures__/drift-record/README.md +5 -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__/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/ci-setup.ts +42 -0
- package/src/components/capability-plugin.test.ts +26 -14
- package/src/components/capability-plugin.ts +185 -31
- package/src/components/compose-group.ts +1 -1
- package/src/components/pr-loop-compose.tofu.e2e.test.ts +1 -1
- package/src/components/pr-loop.tofu.test.ts +1 -1
- 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/cone.ts +138 -0
- package/src/cost-hold.ts +29 -0
- package/src/describe-resources.engine-facts.test.ts +2 -3
- 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/index.ts +3 -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/lock-rows.test.ts +70 -0
- package/src/lock-rows.ts +82 -0
- package/src/lock-timeout.ts +34 -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/terraform.ts +46 -5
- 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/consumers.test.ts +57 -0
- package/src/pin/consumers.ts +95 -0
- package/src/plugin.ts +6 -0
- 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/state/azure-blob.ts +6 -221
- package/src/state/gcs.ts +5 -252
- package/src/state/object-store.ts +4 -32
- package/src/state/s3.ts +10 -182
- package/src/state/store.ts +2 -2
- package/src/state-ops.test.ts +182 -0
- package/src/state-ops.ts +462 -0
- package/src/state-reads.ts +116 -0
- package/src/used-sources.test.ts +75 -0
- package/src/used-sources.ts +204 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A provider network mirror served from the module registry's bucket
|
|
3
|
+
* (chant#4564), and the lock-file grouping terragucci's image recipe uses for
|
|
4
|
+
* its `filesystem_mirror` (moved from terragucci `image-recipe.ts:225-232`,
|
|
5
|
+
* 18f6431595), so both build from one implementation.
|
|
6
|
+
*
|
|
7
|
+
* The network mirror protocol is the layout `providers mirror` writes, served
|
|
8
|
+
* over HTTPS (https://developer.hashicorp.com/terraform/internals/provider-network-mirror-protocol):
|
|
9
|
+
*
|
|
10
|
+
* <host>/<namespace>/<type>/index.json {"versions": {"<v>": {}}}
|
|
11
|
+
* <host>/<namespace>/<type>/<v>.json {"archives": {"<os>_<arch>": {"url": ..., "hashes": ["h1:...", "zh:..."]}}}
|
|
12
|
+
* <host>/<namespace>/<type>/terraform-provider-<type>_<v>_<os>_<arch>.zip
|
|
13
|
+
*
|
|
14
|
+
* `buildMirror` writes one module per distinct lock file, each holding its
|
|
15
|
+
* providers at exactly the locked version beside that lock file, and runs
|
|
16
|
+
* `providers mirror` on each for the platforms asked for, so the lock file's
|
|
17
|
+
* hashes check every download. `uploadMirror` puts the result under the
|
|
18
|
+
* registry's prefix at `providers/`, archives first and `index.json` last, and
|
|
19
|
+
* merges each `index.json` with the one already in the bucket.
|
|
20
|
+
*
|
|
21
|
+
* Lock files keep the host the provider came from. A hostless source resolves
|
|
22
|
+
* to registry.terraform.io under Terraform and to registry.opentofu.org under
|
|
23
|
+
* OpenTofu and choudoufu, so by default each provider locked on one of those
|
|
24
|
+
* two hosts is mirrored under the other as well.
|
|
25
|
+
*/
|
|
26
|
+
import { spawnSync } from "node:child_process";
|
|
27
|
+
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
28
|
+
import { tmpdir } from "node:os";
|
|
29
|
+
import { join, posix, relative } from "node:path";
|
|
30
|
+
import { keyFor } from "./address.js";
|
|
31
|
+
import { PROVIDER_MIRROR_PREFIX } from "./mirror-paths.js";
|
|
32
|
+
export { PROVIDER_MIRROR_PREFIX, isMirrorVersionPath } from "./mirror-paths.js";
|
|
33
|
+
export const LOCK_FILE = ".terraform.lock.hcl";
|
|
34
|
+
/** The two hosts a hostless provider source resolves to. */
|
|
35
|
+
export const TERRAFORM_REGISTRY = "registry.terraform.io";
|
|
36
|
+
export const OPENTOFU_REGISTRY = "registry.opentofu.org";
|
|
37
|
+
/** Directories a lock file is never looked for in: caches and what init or a package manager writes. */
|
|
38
|
+
const SKIP = new Set([".git", ".terraform", ".terragrunt-cache", "node_modules", "cdktf.out", ".terragucci"]);
|
|
39
|
+
/** Every provider in a lock file, by its full address, with its version. */
|
|
40
|
+
export function readLock(text) {
|
|
41
|
+
const out = new Map();
|
|
42
|
+
for (const m of text.matchAll(/provider\s+"([^"]+)"\s*\{([^}]*)\}/g)) {
|
|
43
|
+
const v = /\bversion\s*=\s*"([^"]+)"/.exec(m[2]);
|
|
44
|
+
if (v)
|
|
45
|
+
out.set(m[1], v[1]);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
/** The repo's lock files, as `/`-separated paths relative to the repo, sorted. */
|
|
50
|
+
export function lockFiles(repo) {
|
|
51
|
+
const out = [];
|
|
52
|
+
const walk = (dir) => {
|
|
53
|
+
let names;
|
|
54
|
+
try {
|
|
55
|
+
names = readdirSync(dir);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
for (const n of names) {
|
|
61
|
+
const p = join(dir, n);
|
|
62
|
+
if (n === LOCK_FILE) {
|
|
63
|
+
out.push(posix.normalize(relative(repo, p).split("\\").join("/")));
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (SKIP.has(n))
|
|
67
|
+
continue;
|
|
68
|
+
let dirLike = false;
|
|
69
|
+
try {
|
|
70
|
+
dirLike = statSync(p).isDirectory();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
dirLike = false;
|
|
74
|
+
}
|
|
75
|
+
if (dirLike)
|
|
76
|
+
walk(p);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
walk(repo);
|
|
80
|
+
return out.sort();
|
|
81
|
+
}
|
|
82
|
+
/** Every provider every lock file pins. */
|
|
83
|
+
export function lockEntries(repo, files = lockFiles(repo)) {
|
|
84
|
+
return files.flatMap((lock) => [...readLock(readFileSync(join(repo, lock), "utf-8"))].map(([address, version]) => ({ lock, address, version })));
|
|
85
|
+
}
|
|
86
|
+
const byKey = ([a], [b]) => (a < b ? -1 : a > b ? 1 : 0);
|
|
87
|
+
/** The repo's lock files grouped by what they pin; a lock file that pins nothing is left out. */
|
|
88
|
+
export function mirrorGroups(repo, locks = lockFiles(repo)) {
|
|
89
|
+
const groups = new Map();
|
|
90
|
+
for (const lock of locks) {
|
|
91
|
+
const text = readFileSync(join(repo, lock), "utf-8");
|
|
92
|
+
const providers = readLock(text);
|
|
93
|
+
if (providers.size === 0)
|
|
94
|
+
continue;
|
|
95
|
+
const key = [...providers].sort(byKey).map(([a, v]) => `${a}=${v}`).join("\n");
|
|
96
|
+
const g = groups.get(key);
|
|
97
|
+
if (g)
|
|
98
|
+
g.locks.push(lock);
|
|
99
|
+
else
|
|
100
|
+
groups.set(key, { locks: [lock], text, providers });
|
|
101
|
+
}
|
|
102
|
+
return [...groups.values()];
|
|
103
|
+
}
|
|
104
|
+
/** The other default host's address for a provider locked on one of them, or undefined. */
|
|
105
|
+
export function otherHostAddress(address) {
|
|
106
|
+
const [host, ...rest] = address.split("/");
|
|
107
|
+
if (rest.length !== 2)
|
|
108
|
+
return undefined;
|
|
109
|
+
const h = host.toLowerCase();
|
|
110
|
+
if (h === TERRAFORM_REGISTRY)
|
|
111
|
+
return [OPENTOFU_REGISTRY, ...rest].join("/");
|
|
112
|
+
if (h === OPENTOFU_REGISTRY)
|
|
113
|
+
return [TERRAFORM_REGISTRY, ...rest].join("/");
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* One module per group: `versions.tf` holding each provider at exactly its
|
|
118
|
+
* locked version, and the lock file beside it so `providers mirror` checks the
|
|
119
|
+
* hashes. With `bothHosts`, the other default host's address is required too;
|
|
120
|
+
* the lock file does not name it, so its download is checked by that
|
|
121
|
+
* registry's signature alone.
|
|
122
|
+
*/
|
|
123
|
+
export function mirrorModules(groups, options = {}) {
|
|
124
|
+
const base = options.dir ?? "mirror";
|
|
125
|
+
const files = [];
|
|
126
|
+
groups.forEach((g, i) => {
|
|
127
|
+
const dir = `${base}/${String(i + 1).padStart(2, "0")}`;
|
|
128
|
+
const wanted = new Map(g.providers);
|
|
129
|
+
if (options.bothHosts) {
|
|
130
|
+
for (const [address, version] of g.providers) {
|
|
131
|
+
const other = otherHostAddress(address);
|
|
132
|
+
if (other && ![...wanted.keys()].some((a) => a.toLowerCase() === other.toLowerCase()))
|
|
133
|
+
wanted.set(other, version);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const required = [...wanted].sort(byKey).map(([address, version], n) => ` p${n} = { source = "${address}", version = "= ${version}" }`);
|
|
137
|
+
files.push({
|
|
138
|
+
path: `${dir}/versions.tf`,
|
|
139
|
+
content: [`# The providers ${g.locks.join(", ")} lock${g.locks.length === 1 ? "s" : ""}, each at exactly its locked version.`, "terraform {", " required_providers {", ...required, " }", "}", ""].join("\n"),
|
|
140
|
+
});
|
|
141
|
+
files.push({ path: `${dir}/${LOCK_FILE}`, content: g.text });
|
|
142
|
+
});
|
|
143
|
+
return files;
|
|
144
|
+
}
|
|
145
|
+
/** A platform as the binaries name it, `linux_amd64`, from that form or Docker's `linux/amd64`. */
|
|
146
|
+
export function binaryPlatform(platform) {
|
|
147
|
+
const p = platform.trim().toLowerCase().replace("/", "_");
|
|
148
|
+
if (!/^[a-z0-9]+_[a-z0-9]+$/.test(p))
|
|
149
|
+
throw new Error(`${platform}: name a platform as os_arch or os/arch, such as linux_amd64`);
|
|
150
|
+
return p;
|
|
151
|
+
}
|
|
152
|
+
/** The platform this process runs on, as the binaries name it. */
|
|
153
|
+
export function hostPlatform(platform = process.platform, arch = process.arch) {
|
|
154
|
+
const a = arch === "x64" ? "amd64" : arch === "ia32" ? "386" : arch;
|
|
155
|
+
return `${platform}_${a}`;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Whether a mirror directory holds the provider at the version for the
|
|
159
|
+
* platform, in either layout a `filesystem_mirror` reads: packed (what
|
|
160
|
+
* `providers mirror` writes, a zip per platform) or unpacked (a directory per
|
|
161
|
+
* platform).
|
|
162
|
+
*/
|
|
163
|
+
export function mirrorHas(mirror, address, version, platform) {
|
|
164
|
+
const parts = address.split("/");
|
|
165
|
+
const type = parts[parts.length - 1];
|
|
166
|
+
const dir = join(mirror, ...parts);
|
|
167
|
+
return existsSync(join(dir, `terraform-provider-${type}_${version}_${platform}.zip`)) || existsSync(join(dir, version, platform));
|
|
168
|
+
}
|
|
169
|
+
const defaultRun = (bin, args, cwd) => {
|
|
170
|
+
const r = spawnSync(bin, args, { cwd, encoding: "utf-8", env: { ...process.env, TF_IN_AUTOMATION: "1", TF_INPUT: "0", CHECKPOINT_DISABLE: "1" } });
|
|
171
|
+
return { status: r.status, output: `${r.stdout ?? ""}${r.stderr ?? ""}${r.error ? r.error.message : ""}` };
|
|
172
|
+
};
|
|
173
|
+
export class MirrorBuildError extends Error {
|
|
174
|
+
constructor(message) {
|
|
175
|
+
super(message);
|
|
176
|
+
this.name = "MirrorBuildError";
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** The versions every `<version>.json` beside an `index.json` names, as the index lists them. */
|
|
180
|
+
const indexFor = (dir) => {
|
|
181
|
+
const versions = readdirSync(dir)
|
|
182
|
+
.filter((f) => f.endsWith(".json") && f !== "index.json")
|
|
183
|
+
.map((f) => f.slice(0, -".json".length))
|
|
184
|
+
.sort();
|
|
185
|
+
return `${JSON.stringify({ versions: Object.fromEntries(versions.map((v) => [v, {}])) }, null, 2)}\n`;
|
|
186
|
+
};
|
|
187
|
+
/** Every provider directory (`<host>/<ns>/<type>`) under a mirror. */
|
|
188
|
+
const providerDirs = (out) => {
|
|
189
|
+
const dirs = [];
|
|
190
|
+
const list = (d) => (existsSync(d) ? readdirSync(d).filter((n) => statSync(join(d, n)).isDirectory()) : []);
|
|
191
|
+
for (const host of list(out))
|
|
192
|
+
for (const ns of list(join(out, host)))
|
|
193
|
+
for (const type of list(join(out, host, ns)))
|
|
194
|
+
dirs.push(join(out, host, ns, type));
|
|
195
|
+
return dirs;
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Runs `providers mirror` over every lock file's group into `out`, then
|
|
199
|
+
* rewrites each `index.json` from the version files present, so groups that
|
|
200
|
+
* mirror different versions of one provider all show.
|
|
201
|
+
*/
|
|
202
|
+
export function buildMirror(repo, out, options = {}) {
|
|
203
|
+
const binary = options.binary ?? "tofu";
|
|
204
|
+
const platforms = [...new Set((options.platforms?.length ? options.platforms : ["linux_amd64"]).map(binaryPlatform))];
|
|
205
|
+
const bothHosts = options.bothHosts ?? true;
|
|
206
|
+
const groups = mirrorGroups(repo, options.locks ?? lockFiles(repo));
|
|
207
|
+
const work = options.workDir ?? mkdtempSync(join(tmpdir(), "chant-provider-mirror-"));
|
|
208
|
+
const run = options.run ?? defaultRun;
|
|
209
|
+
const modules = mirrorModules(groups, { dir: "mirror", bothHosts });
|
|
210
|
+
for (const f of modules) {
|
|
211
|
+
mkdirSync(join(work, posix.dirname(f.path)), { recursive: true });
|
|
212
|
+
writeFileSync(join(work, f.path), f.content);
|
|
213
|
+
}
|
|
214
|
+
mkdirSync(out, { recursive: true });
|
|
215
|
+
const dirs = [...new Set(modules.map((f) => posix.dirname(f.path)))];
|
|
216
|
+
for (const d of dirs) {
|
|
217
|
+
const r = run(binary, [`-chdir=${join(work, d)}`, "providers", "mirror", ...platforms.map((p) => `-platform=${p}`), out], repo);
|
|
218
|
+
if (r.status !== 0)
|
|
219
|
+
throw new MirrorBuildError(`${binary} providers mirror for ${d} failed (exit ${r.status}):\n${r.output.trim()}`);
|
|
220
|
+
}
|
|
221
|
+
for (const d of providerDirs(out))
|
|
222
|
+
writeFileSync(join(d, "index.json"), indexFor(d));
|
|
223
|
+
const providers = new Set();
|
|
224
|
+
for (const g of groups) {
|
|
225
|
+
for (const [address, version] of g.providers) {
|
|
226
|
+
providers.add(`${address} ${version}`);
|
|
227
|
+
const other = bothHosts ? otherHostAddress(address) : undefined;
|
|
228
|
+
if (other)
|
|
229
|
+
providers.add(`${other} ${version}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return { out, locks: groups.flatMap((g) => g.locks).sort(), providers: [...providers].sort(), platforms };
|
|
233
|
+
}
|
|
234
|
+
const contentTypeOf = (name) => (name.endsWith(".json") ? "application/json" : name.endsWith(".zip") ? "application/zip" : "application/octet-stream");
|
|
235
|
+
/** Every file under a directory, relative to it, `/`-separated. */
|
|
236
|
+
const walkFiles = (root) => {
|
|
237
|
+
const out = [];
|
|
238
|
+
const walk = (d) => {
|
|
239
|
+
for (const n of readdirSync(d)) {
|
|
240
|
+
const p = join(d, n);
|
|
241
|
+
if (statSync(p).isDirectory())
|
|
242
|
+
walk(p);
|
|
243
|
+
else
|
|
244
|
+
out.push(relative(root, p).split("\\").join("/"));
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
if (existsSync(root))
|
|
248
|
+
walk(root);
|
|
249
|
+
return out.sort();
|
|
250
|
+
};
|
|
251
|
+
/** The key a mirror file goes to. */
|
|
252
|
+
export const mirrorKey = (prefix, path) => keyFor(prefix, `${PROVIDER_MIRROR_PREFIX}/${path}`);
|
|
253
|
+
/** The versions an `index.json` lists. */
|
|
254
|
+
const indexVersions = (text) => {
|
|
255
|
+
if (!text)
|
|
256
|
+
return [];
|
|
257
|
+
try {
|
|
258
|
+
return Object.keys(JSON.parse(text).versions ?? {});
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
/**
|
|
265
|
+
* Uploads a built mirror: archives, then each `<version>.json`, then each
|
|
266
|
+
* `index.json` merged with the versions the store's copy already lists, so a
|
|
267
|
+
* reader never sees an index naming a version whose files are not there yet.
|
|
268
|
+
*/
|
|
269
|
+
export async function uploadMirror(store, dir, options = {}) {
|
|
270
|
+
const files = walkFiles(dir);
|
|
271
|
+
const rank = (f) => (f.endsWith("/index.json") ? 2 : f.endsWith(".json") ? 1 : 0);
|
|
272
|
+
const ordered = [...files].sort((a, b) => rank(a) - rank(b) || (a < b ? -1 : a > b ? 1 : 0));
|
|
273
|
+
const put = [];
|
|
274
|
+
const kept = [];
|
|
275
|
+
for (const f of ordered) {
|
|
276
|
+
const key = mirrorKey(options.prefix, f);
|
|
277
|
+
if (rank(f) === 0 && store.has && (await store.has(key))) {
|
|
278
|
+
kept.push(key);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
let body = readFileSync(join(dir, f));
|
|
282
|
+
if (rank(f) === 2) {
|
|
283
|
+
const versions = [...new Set([...indexVersions((await store.read(key)).body), ...indexVersions(body.toString("utf-8"))])].sort();
|
|
284
|
+
body = `${JSON.stringify({ versions: Object.fromEntries(versions.map((v) => [v, {}])) }, null, 2)}\n`;
|
|
285
|
+
}
|
|
286
|
+
await store.put(key, body, contentTypeOf(f));
|
|
287
|
+
put.push(key);
|
|
288
|
+
}
|
|
289
|
+
return { put, kept };
|
|
290
|
+
}
|
|
291
|
+
/** The mirror's URL under a registry's base URL, ending in `/`. */
|
|
292
|
+
export const mirrorUrl = (registryUrl, prefix) => `${registryUrl.replace(/\/+$/, "")}/${mirrorKey(prefix, "")}`;
|
|
293
|
+
/**
|
|
294
|
+
* The CLI config block users add: the mirrored providers come only from the
|
|
295
|
+
* mirror, and everything else directly from its registry. `include` keeps
|
|
296
|
+
* the mirror from being asked for a provider it lacks, and `exclude` keeps a
|
|
297
|
+
* mirrored provider from going direct.
|
|
298
|
+
*/
|
|
299
|
+
export function mirrorCliConfig(url, addresses) {
|
|
300
|
+
// Takes addresses, or BuiltMirror.providers' `address version` lines.
|
|
301
|
+
const list = [...new Set(addresses.map((a) => a.trim().split(/\s+/)[0]))].sort();
|
|
302
|
+
const quoted = list.map((a) => ` "${a}",`);
|
|
303
|
+
return [
|
|
304
|
+
"provider_installation {",
|
|
305
|
+
" network_mirror {",
|
|
306
|
+
` url = "${url.endsWith("/") ? url : `${url}/`}"`,
|
|
307
|
+
" include = [",
|
|
308
|
+
...quoted,
|
|
309
|
+
" ]",
|
|
310
|
+
" }",
|
|
311
|
+
" direct {",
|
|
312
|
+
" exclude = [",
|
|
313
|
+
...quoted,
|
|
314
|
+
" ]",
|
|
315
|
+
" }",
|
|
316
|
+
"}",
|
|
317
|
+
"",
|
|
318
|
+
].join("\n");
|
|
319
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** The upstream a proxy stands for when an entry names none. */
|
|
2
|
+
export declare const DEFAULT_PROXY_UPSTREAM = "registry.terraform.io";
|
|
3
|
+
/** One registry proxy. */
|
|
4
|
+
export interface RegistryProxy {
|
|
5
|
+
/** The host sources through the proxy name: Artifactory's host, or the label of a Nexus host block. */
|
|
6
|
+
host: string;
|
|
7
|
+
/** Artifactory: the remote or virtual repository's key, joined to the namespace with `__`. Absent for Nexus. */
|
|
8
|
+
repo?: string;
|
|
9
|
+
/** The registry host the proxy stands for. Default registry.terraform.io. */
|
|
10
|
+
upstream?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The modules.v1 URL a CLI host block gives the label (a Nexus proxy or
|
|
13
|
+
* group repository's `<url>/repository/<repo>/v1/modules/`), read in
|
|
14
|
+
* place of the label's service discovery.
|
|
15
|
+
*/
|
|
16
|
+
modules?: string;
|
|
17
|
+
/** The providers.v1 URL of the same host block, when reads of providers go through it. */
|
|
18
|
+
providers?: string;
|
|
19
|
+
}
|
|
20
|
+
/** The `__` an Artifactory source joins its repository key and namespace with. */
|
|
21
|
+
export declare const ARTIFACTORY_JOINER = "__";
|
|
22
|
+
/** A proxy entry for an Artifactory remote or virtual Terraform repository. */
|
|
23
|
+
export declare const artifactoryProxy: (host: string, repo: string, upstream?: string) => RegistryProxy;
|
|
24
|
+
/**
|
|
25
|
+
* A proxy entry for a Nexus proxy or group Terraform repository at `url`.
|
|
26
|
+
* The label defaults to the upstream, as Sonatype's docs write the host
|
|
27
|
+
* block (`host "registry.terraform.io"`), so plain sources go through it.
|
|
28
|
+
*/
|
|
29
|
+
export declare function nexusProxy(o: {
|
|
30
|
+
url: string;
|
|
31
|
+
repo: string;
|
|
32
|
+
upstream?: string;
|
|
33
|
+
host?: string;
|
|
34
|
+
}): RegistryProxy;
|
|
35
|
+
/** The bare labels the proxies use as hosts, so a source starting with one parses as a registry source. */
|
|
36
|
+
export declare const proxyLabels: (proxies?: readonly RegistryProxy[]) => string[];
|
|
37
|
+
/**
|
|
38
|
+
* The upstream registry source a module source written through a proxy
|
|
39
|
+
* stands for, `<upstream>/<ns>/<name>/<system>` with any `//subdir` kept and
|
|
40
|
+
* the query dropped, and the proxy it went through. Undefined for a source
|
|
41
|
+
* through none of them. A Nexus label that is the upstream itself gives the
|
|
42
|
+
* source back at the upstream.
|
|
43
|
+
*/
|
|
44
|
+
export declare function moduleThroughProxy(source: string, proxies?: readonly RegistryProxy[]): {
|
|
45
|
+
proxy: RegistryProxy;
|
|
46
|
+
upstream: string;
|
|
47
|
+
} | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* The upstream provider address a provider source written through a proxy
|
|
50
|
+
* stands for, `<upstream>/<ns>/<type>` in lower case. Undefined for a
|
|
51
|
+
* source through none of them.
|
|
52
|
+
*/
|
|
53
|
+
export declare function providerThroughProxy(source: string, proxies?: readonly RegistryProxy[]): {
|
|
54
|
+
proxy: RegistryProxy;
|
|
55
|
+
upstream: string;
|
|
56
|
+
} | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Where a read of a module at `host/<ns>/<name>/<system>` goes when a proxy
|
|
59
|
+
* stands for `host`: the host and namespace the proxy names it by, and the
|
|
60
|
+
* modules.v1 URL to read in place of discovery (a Nexus host block's).
|
|
61
|
+
* Undefined when no proxy stands for `host`, or the module already names a
|
|
62
|
+
* proxy's host.
|
|
63
|
+
*/
|
|
64
|
+
export declare function proxyRoute(s: {
|
|
65
|
+
host: string;
|
|
66
|
+
namespace: string;
|
|
67
|
+
}, proxies?: readonly RegistryProxy[]): {
|
|
68
|
+
proxy: RegistryProxy;
|
|
69
|
+
host: string;
|
|
70
|
+
namespace: string;
|
|
71
|
+
modules?: string;
|
|
72
|
+
} | undefined;
|
|
73
|
+
//# sourceMappingURL=proxies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxies.d.ts","sourceRoot":"","sources":["../../src/registry/proxies.ts"],"names":[],"mappings":"AAqBA,gEAAgE;AAChE,eAAO,MAAM,sBAAsB,0BAA0B,CAAC;AAE9D,0BAA0B;AAC1B,MAAM,WAAW,aAAa;IAC5B,uGAAuG;IACvG,IAAI,EAAE,MAAM,CAAC;IACb,gHAAgH;IAChH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,kFAAkF;AAClF,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,SAAU,MAAM,QAAQ,MAAM,aAAa,MAAM,KAAG,aAAgH,CAAC;AAElM;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CAK5G;AAKD,2GAA2G;AAC3G,eAAO,MAAM,WAAW,aAAa,SAAS,aAAa,EAAE,KAAQ,MAAM,EAAgC,CAAC;AAS5G;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,aAAa,EAAO,GAAG;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAcjJ;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,aAAa,EAAO,GAAG;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAWnJ;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EACtC,OAAO,GAAE,SAAS,aAAa,EAAO,GACrC;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAOzF"}
|
|
@@ -0,0 +1,111 @@
|
|
|
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.js";
|
|
21
|
+
/** The upstream a proxy stands for when an entry names none. */
|
|
22
|
+
export const DEFAULT_PROXY_UPSTREAM = "registry.terraform.io";
|
|
23
|
+
/** The `__` an Artifactory source joins its repository key and namespace with. */
|
|
24
|
+
export const ARTIFACTORY_JOINER = "__";
|
|
25
|
+
/** A proxy entry for an Artifactory remote or virtual Terraform repository. */
|
|
26
|
+
export const artifactoryProxy = (host, repo, upstream) => ({ host: host.toLowerCase(), repo, ...(upstream ? { upstream: upstream.toLowerCase() } : {}) });
|
|
27
|
+
/**
|
|
28
|
+
* A proxy entry for a Nexus proxy or group Terraform repository at `url`.
|
|
29
|
+
* The label defaults to the upstream, as Sonatype's docs write the host
|
|
30
|
+
* block (`host "registry.terraform.io"`), so plain sources go through it.
|
|
31
|
+
*/
|
|
32
|
+
export function nexusProxy(o) {
|
|
33
|
+
const base = o.url.replace(/\/+$/, "");
|
|
34
|
+
const upstream = (o.upstream ?? DEFAULT_PROXY_UPSTREAM).toLowerCase();
|
|
35
|
+
const repo = encodeURIComponent(o.repo);
|
|
36
|
+
return { host: (o.host ?? upstream).toLowerCase(), upstream, modules: `${base}/repository/${repo}/v1/modules/`, providers: `${base}/repository/${repo}/v1/providers/` };
|
|
37
|
+
}
|
|
38
|
+
const upstreamOf = (p) => (p.upstream ?? DEFAULT_PROXY_UPSTREAM).toLowerCase();
|
|
39
|
+
const sameHost = (a, b) => a.toLowerCase() === b.toLowerCase();
|
|
40
|
+
/** The bare labels the proxies use as hosts, so a source starting with one parses as a registry source. */
|
|
41
|
+
export const proxyLabels = (proxies = []) => proxies.map((p) => p.host);
|
|
42
|
+
/** A namespace through an Artifactory proxy without its `<repo>__`, or undefined when it does not carry that repository. */
|
|
43
|
+
function unjoin(p, namespace) {
|
|
44
|
+
if (!p.repo)
|
|
45
|
+
return namespace;
|
|
46
|
+
const prefix = `${p.repo}${ARTIFACTORY_JOINER}`;
|
|
47
|
+
return namespace.toLowerCase().startsWith(prefix.toLowerCase()) && namespace.length > prefix.length ? namespace.slice(prefix.length) : undefined;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The upstream registry source a module source written through a proxy
|
|
51
|
+
* stands for, `<upstream>/<ns>/<name>/<system>` with any `//subdir` kept and
|
|
52
|
+
* the query dropped, and the proxy it went through. Undefined for a source
|
|
53
|
+
* through none of them. A Nexus label that is the upstream itself gives the
|
|
54
|
+
* source back at the upstream.
|
|
55
|
+
*/
|
|
56
|
+
export function moduleThroughProxy(source, proxies = []) {
|
|
57
|
+
if (proxies.length === 0)
|
|
58
|
+
return undefined;
|
|
59
|
+
const written = source.trim().split("?")[0];
|
|
60
|
+
const [base, ...sub] = written.split("//");
|
|
61
|
+
const s = parseRegistrySource(base, proxyLabels(proxies));
|
|
62
|
+
if (!s)
|
|
63
|
+
return undefined;
|
|
64
|
+
for (const p of proxies) {
|
|
65
|
+
if (!sameHost(p.host, s.host))
|
|
66
|
+
continue;
|
|
67
|
+
const ns = unjoin(p, s.namespace);
|
|
68
|
+
if (ns === undefined)
|
|
69
|
+
continue;
|
|
70
|
+
const upstream = [`${upstreamOf(p)}/${ns}/${s.name}/${s.system}`, ...sub].join("//");
|
|
71
|
+
return { proxy: p, upstream };
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The upstream provider address a provider source written through a proxy
|
|
77
|
+
* stands for, `<upstream>/<ns>/<type>` in lower case. Undefined for a
|
|
78
|
+
* source through none of them.
|
|
79
|
+
*/
|
|
80
|
+
export function providerThroughProxy(source, proxies = []) {
|
|
81
|
+
const parts = source.trim().toLowerCase().split("/");
|
|
82
|
+
if (parts.length !== 3)
|
|
83
|
+
return undefined;
|
|
84
|
+
const [host, namespace, type] = parts;
|
|
85
|
+
for (const p of proxies) {
|
|
86
|
+
if (!sameHost(p.host, host))
|
|
87
|
+
continue;
|
|
88
|
+
const ns = unjoin(p, namespace);
|
|
89
|
+
if (ns === undefined || !ns || !type)
|
|
90
|
+
continue;
|
|
91
|
+
return { proxy: p, upstream: `${upstreamOf(p)}/${ns}/${type}` };
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Where a read of a module at `host/<ns>/<name>/<system>` goes when a proxy
|
|
97
|
+
* stands for `host`: the host and namespace the proxy names it by, and the
|
|
98
|
+
* modules.v1 URL to read in place of discovery (a Nexus host block's).
|
|
99
|
+
* Undefined when no proxy stands for `host`, or the module already names a
|
|
100
|
+
* proxy's host.
|
|
101
|
+
*/
|
|
102
|
+
export function proxyRoute(s, proxies = []) {
|
|
103
|
+
// A source written through a proxy already reads from its host; a Nexus label is resolved by its modules URL.
|
|
104
|
+
const named = proxies.find((p) => sameHost(p.host, s.host));
|
|
105
|
+
if (named)
|
|
106
|
+
return named.modules ? { proxy: named, host: named.host, namespace: s.namespace, modules: named.modules } : undefined;
|
|
107
|
+
const p = proxies.find((q) => sameHost(upstreamOf(q), s.host));
|
|
108
|
+
if (!p)
|
|
109
|
+
return undefined;
|
|
110
|
+
return { proxy: p, host: p.host, namespace: p.repo ? `${p.repo}${ARTIFACTORY_JOINER}${s.namespace}` : s.namespace, ...(p.modules ? { modules: p.modules } : {}) };
|
|
111
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type CredentialsOptions } from "@intentius/chant/terraform/credentials";
|
|
2
|
+
import { type RegistryProxy } from "./proxies.js";
|
|
3
|
+
/** The fetch the reader calls: the global fetch's shape. */
|
|
4
|
+
export type RegistryFetch = (url: string, init?: RequestInit) => Promise<Response>;
|
|
5
|
+
export declare class RegistryError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
/** How a download answer is read. */
|
|
9
|
+
export type DownloadReader = "terraform" | "tofu" | "either";
|
|
10
|
+
export interface RegistryReadOptions {
|
|
11
|
+
fetch?: RegistryFetch;
|
|
12
|
+
/** The URL scheme service discovery is fetched over. Default https, as both CLIs use. */
|
|
13
|
+
scheme?: "https" | "http";
|
|
14
|
+
/**
|
|
15
|
+
* The bearer token sent to the registry's host. Default: the one the CLIs
|
|
16
|
+
* would send (`TF_TOKEN_<host>`, then the credentials files). `false` sends none.
|
|
17
|
+
*/
|
|
18
|
+
token?: string | false;
|
|
19
|
+
/** Where the default token lookup reads (a test passes its own env and files). */
|
|
20
|
+
credentials?: CredentialsOptions;
|
|
21
|
+
/**
|
|
22
|
+
* The modules.v1 URL per registry host, read in place of the host's
|
|
23
|
+
* `/.well-known/terraform.json`, as a CLI's `host "<name>" { services = {...} }`
|
|
24
|
+
* block does. A host named here is taken as a registry host even without a dot.
|
|
25
|
+
*/
|
|
26
|
+
services?: Record<string, string>;
|
|
27
|
+
/**
|
|
28
|
+
* A whole `Authorization` header value sent on every request to the
|
|
29
|
+
* registry, in place of the bearer token, such as Nexus's basic
|
|
30
|
+
* credentials before its bearer form (3.95). Never printed.
|
|
31
|
+
*/
|
|
32
|
+
authorization?: string;
|
|
33
|
+
/** Registry proxies in front of upstream hosts; a read of a module at an upstream goes through its proxy. */
|
|
34
|
+
proxies?: readonly RegistryProxy[];
|
|
35
|
+
}
|
|
36
|
+
/** The token a read sends to a host, by the options. */
|
|
37
|
+
export declare const readToken: (host: string, o: RegistryReadOptions) => string | undefined;
|
|
38
|
+
/** The modules.v1 base a host's service discovery names, ending in `/`. */
|
|
39
|
+
export declare function modulesBase(host: string, options?: RegistryFetch | RegistryReadOptions): Promise<string>;
|
|
40
|
+
/** Every version a registry lists for a module; none when it has no versions file. */
|
|
41
|
+
export declare function registryVersions(source: string, options?: RegistryFetch | RegistryReadOptions): Promise<string[]>;
|
|
42
|
+
export interface RegistryLocationOptions extends RegistryReadOptions {
|
|
43
|
+
/** Which CLI's reading to follow. Default `either`. */
|
|
44
|
+
reads?: DownloadReader;
|
|
45
|
+
}
|
|
46
|
+
/** The location a download answer gives, read as `reads` says, or undefined when it gives none that way. */
|
|
47
|
+
export declare function locationFromAnswer(res: Response, reads?: DownloadReader): Promise<string | undefined>;
|
|
48
|
+
/** Where a registry says one version is, resolved against the download URL when it is relative. */
|
|
49
|
+
export declare function registryLocation(source: string, version: string, options?: RegistryFetch | RegistryLocationOptions): Promise<string>;
|
|
50
|
+
/**
|
|
51
|
+
* The module archive a tarball location holds, uncompressed: the bytes a
|
|
52
|
+
* release's digest is of. The host's token goes along only when the location
|
|
53
|
+
* has no query string; a signed URL carries its own grant, and a bucket
|
|
54
|
+
* refuses a request that has both a signature and an `Authorization` header.
|
|
55
|
+
*/
|
|
56
|
+
export declare function fetchTarball(location: string, options?: RegistryFetch | RegistryReadOptions): Promise<Buffer>;
|
|
57
|
+
//# sourceMappingURL=reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../src/registry/reader.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAkD,KAAK,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAEjI,OAAO,EAAmD,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAGhG,4DAA4D;AAC5D,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAInF,qBAAa,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,qCAAqC;AACrC,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7D,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,yFAAyF;IACzF,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,kFAAkF;IAClF,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6GAA6G;IAC7G,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACpC;AAED,wDAAwD;AACxD,eAAO,MAAM,SAAS,SAAU,MAAM,KAAK,mBAAmB,KAAG,MAAM,GAAG,SAA+F,CAAC;AAqD1K,2EAA2E;AAC3E,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB9G;AAED,sFAAsF;AACtF,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,mBAAmB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAUvH;AAED,MAAM,WAAW,uBAAwB,SAAQ,mBAAmB;IAClE,uDAAuD;IACvD,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,4GAA4G;AAC5G,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,GAAE,cAAyB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAcrH;AAED,mGAAmG;AACnG,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB1I;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAYnH"}
|