@intentius/chant-lexicon-terraform 0.124.52 → 0.124.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- 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/commands.d.ts.map +1 -1
- package/dist/commands.js +4 -3
- package/dist/components/capability-plugin.d.ts +65 -3
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability-plugin.js +197 -13
- 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/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/config.d.ts +59 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +43 -9
- 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/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 +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -2
- package/dist/integrity.json +3 -3
- 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/lsp/option-keys.d.ts.map +1 -1
- package/dist/lsp/option-keys.js +2 -0
- package/dist/manifest.json +1 -1
- 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 +153 -5
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activities/terraform.js +205 -20
- 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/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 +32 -18
- package/dist/report.d.ts +15 -6
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +28 -55
- package/dist/skills/chant-terraform.md +1 -1
- package/dist/state/azure-blob.d.ts +68 -0
- package/dist/state/azure-blob.d.ts.map +1 -0
- package/dist/state/azure-blob.js +201 -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 +62 -0
- package/dist/state/gcs.d.ts.map +1 -0
- package/dist/state/gcs.js +224 -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 +37 -0
- package/dist/state/object-store.d.ts.map +1 -0
- package/dist/state/object-store.js +20 -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 +68 -0
- package/dist/state/s3.d.ts.map +1 -0
- package/dist/state/s3.js +170 -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/terragrunt/affected.d.ts.map +1 -1
- package/dist/terragrunt/affected.js +2 -1
- package/package.json +7 -3
- 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 +11 -0
- package/src/__fixtures__/drift-record/live-plan-sections.json +27 -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/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/commands.ts +4 -3
- package/src/components/capability-plugin.test.ts +172 -5
- package/src/components/capability-plugin.ts +212 -11
- 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 +112 -56
- 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/config-file-listers.test.ts +265 -0
- package/src/config.ts +44 -9
- package/src/describe-resources.engine-facts.test.ts +412 -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/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 +76 -0
- package/src/live-plan-reasons.ts +54 -0
- package/src/lsp/completions.test.ts +1 -1
- package/src/lsp/option-keys.ts +2 -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 +266 -22
- 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/pin/bundle.test.ts +2 -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 +33 -19
- 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 +223 -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 +254 -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 +35 -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 +193 -0
- package/src/state/store.ts +225 -0
- package/src/terragrunt/affected.ts +2 -1
- 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,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every file lister in the lexicon reads the same configuration files for one
|
|
3
|
+
* root, through `./hcl/files.ts` (chant#4457): `.tf` and `.tf.json`, plus
|
|
4
|
+
* `.tofu` and `.tofu.json` when the root runs tofu or choudoufu, with `x.tofu`
|
|
5
|
+
* shadowing `x.tf`. One test per lister that changed. The fast `live` readers
|
|
6
|
+
* also read a `.tf.json` file's `live` block (chant#4504).
|
|
7
|
+
*/
|
|
8
|
+
import { execFileSync } from "node:child_process";
|
|
9
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { afterEach, describe, expect, test } from "vitest";
|
|
13
|
+
import { loadHcl2json } from "@intentius/chant/terraform/hcl2json";
|
|
14
|
+
import { listConfigFiles, readsTofuFiles, selectConfigFiles } from "./hcl/files";
|
|
15
|
+
import { configFiles, liveEstate, localModules, rootState } from "./compose/hcl";
|
|
16
|
+
import { hasCloudBlock, stateObject } from "./state/backend";
|
|
17
|
+
import { estateOfDir } from "./report";
|
|
18
|
+
import { detectLiveEstate, detectLivePolicyVerbs } from "./op/activities/live-detect";
|
|
19
|
+
import { rootWatchPaths } from "./changed-roots";
|
|
20
|
+
import { editPins } from "./pin/edit";
|
|
21
|
+
import { runPinRollout } from "./pin/rollout";
|
|
22
|
+
import { terragruntAffected } from "./terragrunt/affected";
|
|
23
|
+
|
|
24
|
+
const made: string[] = [];
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
for (const d of made.splice(0)) rmSync(d, { recursive: true, force: true });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/** A temporary directory holding `files`, by path relative to it. */
|
|
30
|
+
function tree(files: Record<string, string>): string {
|
|
31
|
+
const dir = mkdtempSync(join(tmpdir(), "chant-4457-"));
|
|
32
|
+
made.push(dir);
|
|
33
|
+
for (const [path, text] of Object.entries(files)) {
|
|
34
|
+
mkdirSync(join(dir, path, ".."), { recursive: true });
|
|
35
|
+
writeFileSync(join(dir, path), text);
|
|
36
|
+
}
|
|
37
|
+
return dir;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe("hcl/files: the one lister", () => {
|
|
41
|
+
test("which binaries read .tofu files", () => {
|
|
42
|
+
expect(readsTofuFiles(undefined)).toBe(false);
|
|
43
|
+
expect(readsTofuFiles("terraform")).toBe(false);
|
|
44
|
+
expect(readsTofuFiles("/opt/bin/terraform")).toBe(false);
|
|
45
|
+
expect(readsTofuFiles("tofu")).toBe(true);
|
|
46
|
+
expect(readsTofuFiles("/usr/local/bin/tofu")).toBe(true);
|
|
47
|
+
expect(readsTofuFiles("choudoufu")).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("the file set follows the binary, and x.tofu shadows x.tf", () => {
|
|
51
|
+
const names = ["main.tf", "main.tofu", "extra.tf.json", "extra.tofu.json", "vars.tf", "notes.md", "terraform.tfvars", "estate.chdf.hcl"];
|
|
52
|
+
expect(selectConfigFiles(names)).toEqual(["extra.tf.json", "main.tf", "vars.tf"]);
|
|
53
|
+
expect(selectConfigFiles(names, "terraform")).toEqual(["extra.tf.json", "main.tf", "vars.tf"]);
|
|
54
|
+
expect(selectConfigFiles(names, "tofu")).toEqual(["extra.tofu.json", "main.tofu", "vars.tf"]);
|
|
55
|
+
expect(selectConfigFiles(names, "choudoufu")).toEqual(["extra.tofu.json", "main.tofu", "vars.tf"]);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("a directory is listed by its files only, not a directory named like one", () => {
|
|
59
|
+
const dir = tree({ "main.tf": "", "a.tofu": "", "odd.tf/inner.txt": "" });
|
|
60
|
+
expect(listConfigFiles(dir)).toEqual(["main.tf"]);
|
|
61
|
+
expect(listConfigFiles(dir, "tofu")).toEqual(["a.tofu", "main.tf"]);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("compose/hcl reads the lister's set", () => {
|
|
66
|
+
test("configFiles follows the binary", () => {
|
|
67
|
+
const dir = tree({ "main.tf": "", "main.tofu": "", "x.tf.json": "{}" });
|
|
68
|
+
expect(configFiles(dir).map((p) => p.slice(dir.length + 1))).toEqual(["main.tf", "x.tf.json"]);
|
|
69
|
+
expect(configFiles(dir, "tofu").map((p) => p.slice(dir.length + 1))).toEqual(["main.tofu", "x.tf.json"]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("a backend in a .tofu file is seen under tofu only", () => {
|
|
73
|
+
const dir = tree({ "backend.tofu": 'terraform {\n backend "gcs" {\n bucket = "b"\n prefix = "p"\n }\n}\n' });
|
|
74
|
+
expect(rootState(dir, "app", { env: {}, recorded: null }).own).toEqual({ key: "local:app/terraform.tfstate" });
|
|
75
|
+
expect(rootState(dir, "app", { env: {}, recorded: null, binary: "tofu" }).own).toEqual({ bucket: "b", key: "p" });
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("a live block in a .tofu file is a choudoufu root's estate", () => {
|
|
79
|
+
const dir = tree({ "main.tofu": 'terraform {\n live {\n estate = "prod"\n }\n}\n' });
|
|
80
|
+
expect(liveEstate(dir)).toEqual({ live: true, estate: "prod" });
|
|
81
|
+
expect(liveEstate(dir, "terraform")).toEqual({ live: false });
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("a local module called from a .tofu file is followed under tofu", () => {
|
|
85
|
+
const dir = tree({ "root/main.tofu": 'module "net" {\n source = "../net"\n}\n', "net/main.tf": "" });
|
|
86
|
+
expect(localModules(join(dir, "root"))).toEqual([]);
|
|
87
|
+
expect(localModules(join(dir, "root"), "tofu")).toEqual([join(dir, "net")]);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("state/backend reads the lister's set", () => {
|
|
92
|
+
test("a cloud block in a .tofu file is refused under tofu, and unseen under terraform", () => {
|
|
93
|
+
const dir = tree({ "main.tofu": 'terraform {\n cloud {\n organization = "o"\n }\n}\n' });
|
|
94
|
+
expect(hasCloudBlock(dir)).toBe(false);
|
|
95
|
+
expect(hasCloudBlock(dir, "tofu")).toBe(true);
|
|
96
|
+
expect(hasCloudBlock(dir, "choudoufu")).toBe(true);
|
|
97
|
+
expect(stateObject(dir, {}, undefined, "tofu")).toMatchObject({ refused: "cloud" });
|
|
98
|
+
expect(stateObject(dir, {}, undefined, "terraform")).toMatchObject({ backend: "local" });
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("a cloud block in a .tofu.json file is seen under tofu", () => {
|
|
102
|
+
const dir = tree({ "main.tofu.json": JSON.stringify({ terraform: { cloud: { organization: "o" } } }) });
|
|
103
|
+
expect(hasCloudBlock(dir, "tofu")).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("report and live-detect read choudoufu's set", () => {
|
|
108
|
+
const live = 'terraform {\n live {\n estate = "prod"\n }\n}\n';
|
|
109
|
+
|
|
110
|
+
test("estateOfDir finds a live block in a .tofu file", () => {
|
|
111
|
+
expect(estateOfDir(tree({ "main.tofu": live }))).toBe("prod");
|
|
112
|
+
expect(estateOfDir(tree({ "main.tofu": live }), "terraform")).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("detectLiveEstate finds a live block in a .tofu file, and x.tofu shadows x.tf", () => {
|
|
116
|
+
expect(detectLiveEstate(tree({ "main.tofu": live }))).toBe("prod");
|
|
117
|
+
expect(detectLiveEstate(tree({ "main.tf": live, "main.tofu": "" }))).toBeUndefined();
|
|
118
|
+
expect(detectLiveEstate(tree({ "main.tf": live, "main.tofu": "" }), "terraform")).toBe("prod");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const liveJson = JSON.stringify({ terraform: { required_version: ">= 1.5.0", live: { estate: "prod" } } });
|
|
122
|
+
|
|
123
|
+
test("estateOfDir finds a live block in a .tf.json file (chant#4504)", () => {
|
|
124
|
+
expect(estateOfDir(tree({ "main.tf.json": liveJson }))).toBe("prod");
|
|
125
|
+
expect(estateOfDir(tree({ "main.tf.json": liveJson }), "terraform")).toBe("prod");
|
|
126
|
+
// JSON syntax may write the terraform block as an array of bodies.
|
|
127
|
+
expect(estateOfDir(tree({ "main.tf.json": JSON.stringify({ terraform: [{}, { live: [{ estate: "second" }] }] }) }))).toBe("second");
|
|
128
|
+
expect(estateOfDir(tree({ "main.tf.json": JSON.stringify({ terraform: { live: { estate: "" } } }) }))).toBeUndefined();
|
|
129
|
+
expect(estateOfDir(tree({ "main.tf.json": "{ not json" }))).toBeUndefined();
|
|
130
|
+
// A live key outside the terraform block is not a live block.
|
|
131
|
+
expect(estateOfDir(tree({ "main.tf.json": JSON.stringify({ live: { estate: "prod" } }) }))).toBeUndefined();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("estateOfDir takes the first live block in filename order across .tf and .tf.json", () => {
|
|
135
|
+
expect(estateOfDir(tree({ "a.tf.json": liveJson, "b.tf": live.replace("prod", "other") }))).toBe("prod");
|
|
136
|
+
expect(estateOfDir(tree({ "a.tf": live.replace("prod", "other"), "b.tf.json": liveJson }))).toBe("other");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("estateOfDir reads a .tofu.json live block under choudoufu, and x.tofu.json shadows x.tf.json", () => {
|
|
140
|
+
expect(estateOfDir(tree({ "main.tofu.json": liveJson }))).toBe("prod");
|
|
141
|
+
expect(estateOfDir(tree({ "main.tofu.json": liveJson }), "terraform")).toBeUndefined();
|
|
142
|
+
expect(estateOfDir(tree({ "main.tf.json": liveJson, "main.tofu.json": "{}" }))).toBeUndefined();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("detectLiveEstate finds a live block in a .tf.json file (chant#4504)", () => {
|
|
146
|
+
expect(detectLiveEstate(tree({ "main.tf.json": liveJson }))).toBe("prod");
|
|
147
|
+
expect(detectLiveEstate(tree({ "main.tf.json": liveJson }), "terraform")).toBe("prod");
|
|
148
|
+
expect(detectLiveEstate(tree({ "main.tofu.json": liveJson }))).toBe("prod");
|
|
149
|
+
expect(detectLiveEstate(tree({ "main.tf.json": JSON.stringify({ terraform: { live: { estate: "" } } }) }))).toBeUndefined();
|
|
150
|
+
expect(detectLiveEstate(tree({ "main.tf.json": "{ not json" }))).toBeUndefined();
|
|
151
|
+
// The sidecar still comes first.
|
|
152
|
+
expect(detectLiveEstate(tree({ "estate.chdf.hcl": 'estate = "side"\n', "main.tf.json": liveJson }))).toBe("side");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("detectLivePolicyVerbs reads a .tf.json live block's policy, as an object or an array", () => {
|
|
156
|
+
const policy = { undeclared_tagged: "adopt", undeclared_untagged: "report" };
|
|
157
|
+
expect(detectLivePolicyVerbs(tree({ "main.tf.json": JSON.stringify({ terraform: { live: { estate: "prod", policy } } }) }))).toEqual({
|
|
158
|
+
undeclaredTagged: "adopt",
|
|
159
|
+
undeclaredUntagged: "report",
|
|
160
|
+
});
|
|
161
|
+
expect(detectLivePolicyVerbs(tree({ "main.tf.json": JSON.stringify({ terraform: { live: { estate: "prod", policy: [{ undeclared_untagged: "delete" }] } } }) }))).toEqual({
|
|
162
|
+
undeclaredTagged: undefined,
|
|
163
|
+
undeclaredUntagged: "delete",
|
|
164
|
+
});
|
|
165
|
+
expect(detectLivePolicyVerbs(tree({ "main.tf.json": liveJson }))).toBeUndefined();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe("pin rollout's listers follow the binary (chant#4504)", () => {
|
|
170
|
+
const REGISTRY = "app.terraform.io/acme/vpc/aws";
|
|
171
|
+
const call = (version: string) => `module "vpc" {\n source = "${REGISTRY}"\n version = "${version}"\n}\n`;
|
|
172
|
+
|
|
173
|
+
/** A git repository on `main` holding `files`, committed. */
|
|
174
|
+
function repo(files: Record<string, string>): string {
|
|
175
|
+
const dir = tree(files);
|
|
176
|
+
const git = (...args: string[]) =>
|
|
177
|
+
execFileSync("git", args, { cwd: dir, env: { ...process.env, GIT_AUTHOR_NAME: "t", GIT_AUTHOR_EMAIL: "t@t", GIT_COMMITTER_NAME: "t", GIT_COMMITTER_EMAIL: "t@t" } });
|
|
178
|
+
git("init", "--quiet", "--initial-branch=main");
|
|
179
|
+
git("add", ".");
|
|
180
|
+
git("commit", "--quiet", "-m", "fixture");
|
|
181
|
+
return dir;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function roots(cwd: string, binary?: string) {
|
|
185
|
+
const r = await runPinRollout({ cwd, module: REGISTRY, from: "1.3.0", to: "1.4.0", parser: await loadHcl2json(), mode: "report", ...(binary ? { binary } : {}) });
|
|
186
|
+
return Object.fromEntries(r.roots.map((x) => [x.root, { state: x.state, files: x.files }]));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
test("candidateRoots and rootFiles read .tofu files under tofu only, with x.tofu shadowing x.tf", async () => {
|
|
190
|
+
const cwd = repo({
|
|
191
|
+
"only-tofu/main.tofu": call("1.3.0"),
|
|
192
|
+
"shadowed/main.tf": call("1.2.0"),
|
|
193
|
+
"shadowed/main.tofu": call("1.3.0"),
|
|
194
|
+
"plain/main.tf": call("1.3.0"),
|
|
195
|
+
"plain/notes.md": call("1.3.0"),
|
|
196
|
+
});
|
|
197
|
+
expect(await roots(cwd)).toEqual({
|
|
198
|
+
plain: { state: "from", files: ["plain/main.tf"] },
|
|
199
|
+
shadowed: { state: "elsewhere", files: ["shadowed/main.tf"] },
|
|
200
|
+
});
|
|
201
|
+
expect(await roots(cwd, "tofu")).toEqual({
|
|
202
|
+
"only-tofu": { state: "from", files: ["only-tofu/main.tofu"] },
|
|
203
|
+
plain: { state: "from", files: ["plain/main.tf"] },
|
|
204
|
+
shadowed: { state: "from", files: ["shadowed/main.tofu"] },
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("a .tf.json root is a candidate, and its moving call is refused by name", async () => {
|
|
209
|
+
const cwd = repo({ "json/main.tf.json": JSON.stringify({ module: { vpc: { source: REGISTRY, version: "1.3.0" } } }) });
|
|
210
|
+
const r = await runPinRollout({ cwd, module: REGISTRY, from: "1.3.0", to: "1.4.0", parser: await loadHcl2json(), mode: "report" });
|
|
211
|
+
expect(r.roots).toEqual([expect.objectContaining({ root: "json", state: "refused", files: ["json/main.tf.json"], reason: expect.stringContaining("JSON syntax") })]);
|
|
212
|
+
expect(r.waves).toEqual([]);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("terragrunt affected follows a module's .tofu calls (chant#4504)", () => {
|
|
217
|
+
const unit = { path: "live/app", dependencies: [], reading: ["live/app/terragrunt.hcl", "modules/app/main.tofu"] };
|
|
218
|
+
|
|
219
|
+
test("a change under a module called from a .tofu file is a module-call reason", () => {
|
|
220
|
+
const dir = tree({
|
|
221
|
+
"live/app/terragrunt.hcl": 'terraform {\n source = "../../modules/app"\n}\n',
|
|
222
|
+
"modules/app/main.tofu": 'module "net" {\n source = "../net"\n}\n',
|
|
223
|
+
"modules/net/main.tf": "",
|
|
224
|
+
});
|
|
225
|
+
expect(terragruntAffected({ dir, units: [unit], terragruntSelected: [], changedFiles: ["modules/net/main.tf"] })).toEqual({
|
|
226
|
+
units: [{ path: "live/app", reasons: [{ kind: "module-call", files: ["modules/net/main.tf"], via: "modules/net" }] }],
|
|
227
|
+
notes: [],
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("a call in an x.tf that x.tofu shadows is not followed", () => {
|
|
232
|
+
const dir = tree({
|
|
233
|
+
"live/app/terragrunt.hcl": 'terraform {\n source = "../../modules/app"\n}\n',
|
|
234
|
+
"modules/app/main.tofu": "",
|
|
235
|
+
"modules/app/main.tf": 'module "old" {\n source = "../old"\n}\n',
|
|
236
|
+
"modules/old/main.tf": "",
|
|
237
|
+
});
|
|
238
|
+
expect(terragruntAffected({ dir, units: [unit], terragruntSelected: [], changedFiles: ["modules/old/main.tf"] })).toEqual({ units: [], notes: [] });
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe("changed-roots reads the lister's set", () => {
|
|
243
|
+
test("a module called from a .tofu file is watched for a tofu root only", () => {
|
|
244
|
+
const dir = tree({ "app/main.tofu": 'module "net" {\n source = "../modules/net"\n}\n', "modules/net/main.tf": "" });
|
|
245
|
+
expect(rootWatchPaths(dir, { dir: "app" })).toEqual(["app"]);
|
|
246
|
+
expect(rootWatchPaths(dir, { dir: "app" }, "tofu")).toEqual(["app", "modules/net"]);
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe("pin edit on a JSON-syntax file", () => {
|
|
251
|
+
const REGISTRY = "app.terraform.io/acme/vpc/aws";
|
|
252
|
+
|
|
253
|
+
test("a call that would move is refused by name, and the file is left as written", async () => {
|
|
254
|
+
const text = JSON.stringify({ module: { vpc: { source: REGISTRY, version: "1.3.0" } } });
|
|
255
|
+
const result = await editPins(text, "main.tf.json", { module: REGISTRY, from: "1.3.0", to: "1.4.0" }, await loadHcl2json());
|
|
256
|
+
expect(result.content).toBe(text);
|
|
257
|
+
expect(result.calls).toEqual([expect.objectContaining({ outcome: "refused", file: "main.tf.json", call: "module.vpc" })]);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("a call already at the new pin reads as already", async () => {
|
|
261
|
+
const text = JSON.stringify({ module: { vpc: { source: REGISTRY, version: "1.4.0" } } });
|
|
262
|
+
const result = await editPins(text, "main.tofu.json", { module: REGISTRY, from: "1.3.0", to: "1.4.0" }, await loadHcl2json());
|
|
263
|
+
expect(result.calls.map((c) => c.outcome)).toEqual(["already"]);
|
|
264
|
+
});
|
|
265
|
+
});
|
package/src/config.ts
CHANGED
|
@@ -63,8 +63,15 @@ export const terraformRootSchema = z.strictObject({
|
|
|
63
63
|
workspace: z.string().optional(),
|
|
64
64
|
/** `-var-file` arguments, in order, relative to `dir`. */
|
|
65
65
|
varFiles: z.array(z.string()).optional(),
|
|
66
|
-
/** `-backend-config` key/value pairs handed to `init
|
|
66
|
+
/** `-backend-config` key/value pairs handed to `init`, after {@link backendConfigFiles}. */
|
|
67
67
|
backendConfig: z.record(z.string(), z.string()).optional(),
|
|
68
|
+
/**
|
|
69
|
+
* Backend-config files handed to `init` as `-backend-config=<path>`, in
|
|
70
|
+
* order and before the {@link backendConfig} pairs, relative to `dir`
|
|
71
|
+
* (chant#4459). The composition reads their `key = "value"` lines as it
|
|
72
|
+
* reads the pairs, to find a root's state before init.
|
|
73
|
+
*/
|
|
74
|
+
backendConfigFiles: z.array(z.string()).optional(),
|
|
68
75
|
/**
|
|
69
76
|
* The choudoufu estate this root runs against, for a live root whose HCL
|
|
70
77
|
* does not declare one of its own (#2479).
|
|
@@ -109,6 +116,16 @@ export const terraformRootSchema = z.strictObject({
|
|
|
109
116
|
* every root, for one run.
|
|
110
117
|
*/
|
|
111
118
|
observe: terraformObserveDepthSchema.optional(),
|
|
119
|
+
/**
|
|
120
|
+
* The root is declared only so a change to it brings this project into the
|
|
121
|
+
* change (#4484). A dns member that declares another cloud's root, so that
|
|
122
|
+
* an azure change reaches dns, sets it on that root. Change detection
|
|
123
|
+
* (`changedUnits`) still counts the root; the build and every live read
|
|
124
|
+
* skip it, so it is never drawn or read as this project's own: its entities
|
|
125
|
+
* are not built, and `describeResources`, drift and the owned-resource
|
|
126
|
+
* reads leave it out. An Op step that names the root still runs against it.
|
|
127
|
+
*/
|
|
128
|
+
signalOnly: z.boolean().optional(),
|
|
112
129
|
});
|
|
113
130
|
|
|
114
131
|
export const terraformConfigSchema = z.strictObject({
|
|
@@ -145,19 +162,37 @@ export const terraformConfigSchema = z.strictObject({
|
|
|
145
162
|
moduleRoot: z.string().optional(),
|
|
146
163
|
/** The default {@link TerraformRootConfig.observe} for every root that sets none (#4358). */
|
|
147
164
|
observe: terraformObserveDepthSchema.optional(),
|
|
148
|
-
/** Named root modules. The name is the entity-key prefix, so keep it stable. */
|
|
149
|
-
roots: z.record(z.string(), terraformRootSchema).default({}),
|
|
150
165
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
166
|
+
* Composed runs (#4419, #4438), off unless `enabled` is true. When on, the
|
|
167
|
+
* `terraform-apply` components a change reaches, and the ones they read,
|
|
168
|
+
* plan as one plan and apply as one apply of a composed root of `module`
|
|
169
|
+
* calls, so a value one root creates reaches its readers in that apply. A
|
|
170
|
+
* component that cannot compose plans wave by wave as without the setting,
|
|
171
|
+
* and the report says why (`compositionFor` in `compose.ts`).
|
|
172
|
+
*
|
|
173
|
+
* `max` caps how many roots compose; a change over it applies wholly in
|
|
174
|
+
* waves. Default 10.
|
|
156
175
|
*/
|
|
157
|
-
|
|
176
|
+
compose: z
|
|
177
|
+
.strictObject({
|
|
178
|
+
enabled: z.boolean(),
|
|
179
|
+
max: z.number().int().min(2).optional(),
|
|
180
|
+
})
|
|
181
|
+
.optional(),
|
|
182
|
+
/** Named root modules. The name is the entity-key prefix, so keep it stable. */
|
|
183
|
+
roots: z.record(z.string(), terraformRootSchema).default({}),
|
|
158
184
|
});
|
|
159
185
|
|
|
160
186
|
export type TerraformRootConfig = z.infer<typeof terraformRootSchema>;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The roots a project owns: `roots` without the ones marked `signalOnly`
|
|
190
|
+
* (#4484), which only feed change detection. The build and the live reads
|
|
191
|
+
* take these; `changedUnits` takes every root.
|
|
192
|
+
*/
|
|
193
|
+
export function ownRoots<R extends { signalOnly?: boolean }>(roots: Record<string, R> | undefined): Record<string, R> {
|
|
194
|
+
return Object.fromEntries(Object.entries(roots ?? {}).filter(([, r]) => r.signalOnly !== true));
|
|
195
|
+
}
|
|
161
196
|
export type TerraformConfig = z.infer<typeof terraformConfigSchema>;
|
|
162
197
|
|
|
163
198
|
declare module "@intentius/chant/config" {
|