@intentius/chant-lexicon-terraform 0.57.0 → 0.59.0
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 +9 -0
- package/dist/composites/terraform-adopt-op.d.ts +131 -0
- package/dist/composites/terraform-adopt-op.d.ts.map +1 -0
- package/dist/composites/terraform-apply-op.d.ts +70 -17
- package/dist/composites/terraform-apply-op.d.ts.map +1 -1
- package/dist/composites/terraform-watch-op.d.ts +104 -37
- package/dist/composites/terraform-watch-op.d.ts.map +1 -1
- package/dist/config.d.ts +49 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/describe-resources.d.ts +256 -16
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/hcl/descend.d.ts +98 -0
- package/dist/hcl/descend.d.ts.map +1 -0
- package/dist/hcl/marker.d.ts +130 -0
- package/dist/hcl/marker.d.ts.map +1 -0
- package/dist/hcl/parse.d.ts +156 -9
- package/dist/hcl/parse.d.ts.map +1 -1
- package/dist/hcl/references.d.ts +76 -0
- package/dist/hcl/references.d.ts.map +1 -0
- package/dist/hcl/roots.d.ts +22 -3
- package/dist/hcl/roots.d.ts.map +1 -1
- package/dist/hcl/suppressions.d.ts +54 -0
- package/dist/hcl/suppressions.d.ts.map +1 -0
- package/dist/hcl/value.d.ts +77 -0
- package/dist/hcl/value.d.ts.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +34 -5
- package/dist/lint/audit-catalog.d.ts +3 -2
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-lineage.d.ts +95 -0
- package/dist/lint/audit-lineage.d.ts.map +1 -1
- package/dist/lint/post-synth/blocks.d.ts +53 -0
- package/dist/lint/post-synth/blocks.d.ts.map +1 -0
- package/dist/lint/post-synth/fixtures/load.d.ts +37 -0
- package/dist/lint/post-synth/fixtures/load.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/module-source.d.ts +38 -0
- package/dist/lint/post-synth/module-source.d.ts.map +1 -0
- package/dist/lint/post-synth/scope.d.ts +65 -0
- package/dist/lint/post-synth/scope.d.ts.map +1 -0
- package/dist/lint/post-synth/tf001.d.ts +58 -6
- package/dist/lint/post-synth/tf001.d.ts.map +1 -1
- package/dist/lint/post-synth/tf002.d.ts +32 -0
- package/dist/lint/post-synth/tf002.d.ts.map +1 -0
- package/dist/lint/post-synth/tf003.d.ts +22 -0
- package/dist/lint/post-synth/tf003.d.ts.map +1 -0
- package/dist/lint/post-synth/tf004.d.ts +22 -0
- package/dist/lint/post-synth/tf004.d.ts.map +1 -0
- package/dist/lint/post-synth/tf005.d.ts +28 -0
- package/dist/lint/post-synth/tf005.d.ts.map +1 -0
- package/dist/lint/post-synth/tf006.d.ts +21 -0
- package/dist/lint/post-synth/tf006.d.ts.map +1 -0
- package/dist/lint/post-synth/tf007.d.ts +21 -0
- package/dist/lint/post-synth/tf007.d.ts.map +1 -0
- package/dist/lint/post-synth/tf008.d.ts +22 -0
- package/dist/lint/post-synth/tf008.d.ts.map +1 -0
- package/dist/lint/post-synth/tf009.d.ts +24 -0
- package/dist/lint/post-synth/tf009.d.ts.map +1 -0
- package/dist/lint/post-synth/tf010.d.ts +19 -0
- package/dist/lint/post-synth/tf010.d.ts.map +1 -0
- package/dist/lint/post-synth/tf011.d.ts +18 -0
- package/dist/lint/post-synth/tf011.d.ts.map +1 -0
- package/dist/lint/post-synth/tf012.d.ts +17 -0
- package/dist/lint/post-synth/tf012.d.ts.map +1 -0
- package/dist/lint/post-synth/tf013.d.ts +21 -0
- package/dist/lint/post-synth/tf013.d.ts.map +1 -0
- package/dist/lint/post-synth/tf014.d.ts +25 -0
- package/dist/lint/post-synth/tf014.d.ts.map +1 -0
- package/dist/lint/post-synth/tf015.d.ts +30 -0
- package/dist/lint/post-synth/tf015.d.ts.map +1 -0
- package/dist/lint/post-synth/tf016.d.ts +30 -0
- package/dist/lint/post-synth/tf016.d.ts.map +1 -0
- package/dist/lint/post-synth/tf017.d.ts +19 -0
- package/dist/lint/post-synth/tf017.d.ts.map +1 -0
- package/dist/lint/post-synth/tf018.d.ts +20 -0
- package/dist/lint/post-synth/tf018.d.ts.map +1 -0
- package/dist/lint/post-synth/tf019.d.ts +25 -0
- package/dist/lint/post-synth/tf019.d.ts.map +1 -0
- package/dist/lint/post-synth/tf020.d.ts +38 -0
- package/dist/lint/post-synth/tf020.d.ts.map +1 -0
- package/dist/lint/post-synth/tf021.d.ts +41 -0
- package/dist/lint/post-synth/tf021.d.ts.map +1 -0
- package/dist/lint/post-synth/tf022.d.ts +24 -0
- package/dist/lint/post-synth/tf022.d.ts.map +1 -0
- package/dist/lint/post-synth/tf024.d.ts +24 -0
- package/dist/lint/post-synth/tf024.d.ts.map +1 -0
- package/dist/lint/post-synth/tf025.d.ts +29 -0
- package/dist/lint/post-synth/tf025.d.ts.map +1 -0
- package/dist/lint/post-synth/tf026.d.ts +28 -0
- package/dist/lint/post-synth/tf026.d.ts.map +1 -0
- package/dist/lint/post-synth/tf027.d.ts +30 -0
- package/dist/lint/post-synth/tf027.d.ts.map +1 -0
- package/dist/lint/post-synth/tf028.d.ts +33 -0
- package/dist/lint/post-synth/tf028.d.ts.map +1 -0
- package/dist/lint/rules/fixtures/load.d.ts +13 -0
- package/dist/lint/rules/fixtures/load.d.ts.map +1 -0
- package/dist/lint/rules/plan-before-apply.d.ts +11 -0
- package/dist/lint/rules/plan-before-apply.d.ts.map +1 -1
- package/dist/lint/secret-shape.d.ts +68 -0
- package/dist/lint/secret-shape.d.ts.map +1 -0
- package/dist/live-ownership.d.ts +55 -0
- package/dist/live-ownership.d.ts.map +1 -0
- package/dist/lsp/option-keys.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/okf/index.md +26 -0
- package/dist/okf/rules/TF002.md +11 -0
- package/dist/okf/rules/TF003.md +11 -0
- package/dist/okf/rules/TF004.md +11 -0
- package/dist/okf/rules/TF005.md +11 -0
- package/dist/okf/rules/TF006.md +11 -0
- package/dist/okf/rules/TF007.md +11 -0
- package/dist/okf/rules/TF008.md +11 -0
- package/dist/okf/rules/TF009.md +11 -0
- package/dist/okf/rules/TF010.md +11 -0
- package/dist/okf/rules/TF011.md +11 -0
- package/dist/okf/rules/TF012.md +11 -0
- package/dist/okf/rules/TF013.md +11 -0
- package/dist/okf/rules/TF014.md +11 -0
- package/dist/okf/rules/TF015.md +11 -0
- package/dist/okf/rules/TF016.md +11 -0
- package/dist/okf/rules/TF017.md +11 -0
- package/dist/okf/rules/TF018.md +11 -0
- package/dist/okf/rules/TF019.md +11 -0
- package/dist/okf/rules/TF020.md +11 -0
- package/dist/okf/rules/TF021.md +11 -0
- package/dist/okf/rules/TF022.md +11 -0
- package/dist/okf/rules/TF024.md +11 -0
- package/dist/okf/rules/TF025.md +11 -0
- package/dist/okf/rules/TF026.md +11 -0
- package/dist/okf/rules/TF027.md +11 -0
- package/dist/okf/rules/TF028.md +11 -0
- package/dist/op/activities/index.d.ts +13 -6
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/live-detect.d.ts +51 -0
- package/dist/op/activities/live-detect.d.ts.map +1 -0
- package/dist/op/activities/terraform.d.ts +466 -17
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activity-contracts.d.ts +148 -0
- package/dist/op/activity-contracts.d.ts.map +1 -0
- package/dist/op/adoption.d.ts +118 -0
- package/dist/op/adoption.d.ts.map +1 -0
- package/dist/op/builders.d.ts +48 -6
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/resolve-root-mode.d.ts +59 -0
- package/dist/op/resolve-root-mode.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/rules/blocks.ts +109 -0
- package/dist/rules/module-source.ts +121 -0
- package/dist/rules/plan-before-apply.ts +11 -0
- package/dist/rules/scope.ts +107 -0
- package/dist/rules/tf001.ts +98 -18
- package/dist/rules/tf002.ts +151 -0
- package/dist/rules/tf003.ts +71 -0
- package/dist/rules/tf004.ts +63 -0
- package/dist/rules/tf005.ts +76 -0
- package/dist/rules/tf006.ts +53 -0
- package/dist/rules/tf007.ts +78 -0
- package/dist/rules/tf008.ts +75 -0
- package/dist/rules/tf009.ts +59 -0
- package/dist/rules/tf010.ts +51 -0
- package/dist/rules/tf011.ts +49 -0
- package/dist/rules/tf012.ts +47 -0
- package/dist/rules/tf013.ts +63 -0
- package/dist/rules/tf014.ts +72 -0
- package/dist/rules/tf015.ts +72 -0
- package/dist/rules/tf016.ts +75 -0
- package/dist/rules/tf017.ts +50 -0
- package/dist/rules/tf018.ts +72 -0
- package/dist/rules/tf019.ts +72 -0
- package/dist/rules/tf020.ts +123 -0
- package/dist/rules/tf021.ts +126 -0
- package/dist/rules/tf022.ts +61 -0
- package/dist/rules/tf024.ts +70 -0
- package/dist/rules/tf025.ts +101 -0
- package/dist/rules/tf026.ts +84 -0
- package/dist/rules/tf027.ts +74 -0
- package/dist/rules/tf028.ts +96 -0
- package/dist/skills/chant-terraform.md +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/package.json +7 -2
- package/src/__fixtures__/ACCEPTANCE.md +181 -0
- package/src/__fixtures__/live/main.tf +28 -0
- package/src/__fixtures__/live-adopt/estate.chdf.hcl +6 -0
- package/src/__fixtures__/live-adopt/main.tf +21 -0
- package/src/__fixtures__/live-estate/README.md +58 -0
- package/src/__fixtures__/live-estate/compute.tf +10 -0
- package/src/__fixtures__/live-estate/estate.chdf.hcl +1 -0
- package/src/__fixtures__/live-estate/logs.tf +8 -0
- package/src/__fixtures__/live-estate/network.tf +24 -0
- package/src/__fixtures__/live-estate/rule.tf +11 -0
- package/src/__fixtures__/live-estate/unowned.tf +21 -0
- package/src/__fixtures__/live-estate/versions.tf +31 -0
- package/src/__fixtures__/live-ls.json +98 -0
- package/src/__fixtures__/live-plan.json +285 -0
- package/src/__fixtures__/live-sidecar/estate.chdf.hcl +3 -0
- package/src/__fixtures__/live-sidecar/main.tf +24 -0
- package/src/__fixtures__/module-tree/README.md +16 -0
- package/src/__fixtures__/module-tree/cycle/main.tf +3 -0
- package/src/__fixtures__/module-tree/cycle/modules/a/main.tf +9 -0
- package/src/__fixtures__/module-tree/cycle/modules/b/main.tf +9 -0
- package/src/__fixtures__/module-tree/missing/main.tf +9 -0
- package/src/__fixtures__/module-tree/outside/root/main.tf +9 -0
- package/src/__fixtures__/module-tree/root/main.tf +28 -0
- package/src/__fixtures__/module-tree/root/modules/cdn/main.tf +15 -0
- package/src/__fixtures__/module-tree/root/modules/cdn/modules/bucket/main.tf +11 -0
- package/src/__fixtures__/module-tree/shared/main.tf +5 -0
- package/src/__fixtures__/references/main.tf +56 -0
- package/src/__fixtures__/remote-backend/main.tf +34 -0
- package/src/__fixtures__/with-backend/main.tf +7 -0
- package/src/codegen/lint-rules-page.test.ts +45 -0
- package/src/composites/terraform-adopt-op.acceptance.test.ts +193 -0
- package/src/composites/terraform-adopt-op.test.ts +175 -0
- package/src/composites/terraform-adopt-op.ts +202 -0
- package/src/composites/terraform-apply-op.acceptance.test.ts +188 -5
- package/src/composites/terraform-apply-op.test.ts +179 -19
- package/src/composites/terraform-apply-op.ts +123 -30
- package/src/composites/terraform-watch-op.test.ts +169 -29
- package/src/composites/terraform-watch-op.ts +183 -75
- package/src/config.ts +32 -3
- package/src/describe-resources.live.test.ts +598 -0
- package/src/describe-resources.test.ts +53 -0
- package/src/describe-resources.ts +811 -28
- package/src/hcl/descend.test.ts +162 -0
- package/src/hcl/descend.ts +273 -0
- package/src/hcl/marker.test.ts +147 -0
- package/src/hcl/marker.ts +235 -0
- package/src/hcl/parse.test.ts +69 -0
- package/src/hcl/parse.ts +269 -27
- package/src/hcl/references.test.ts +116 -0
- package/src/hcl/references.ts +172 -0
- package/src/hcl/roots.ts +59 -5
- package/src/hcl/suppressions.test.ts +147 -0
- package/src/hcl/suppressions.ts +204 -0
- package/src/hcl/value.test.ts +73 -0
- package/src/hcl/value.ts +106 -0
- package/src/index.ts +31 -1
- package/src/lint/audit-catalog.ts +288 -3
- package/src/lint/audit-lineage.ts +384 -1
- package/src/lint/audit.test.ts +210 -3
- package/src/lint/post-synth/blocks.ts +109 -0
- package/src/lint/post-synth/fixtures/TF001/negative-cloud.tf +28 -0
- package/src/lint/post-synth/fixtures/TF001/negative.tf +30 -0
- package/src/lint/post-synth/fixtures/TF001/positive-local.tf +31 -0
- package/src/lint/post-synth/fixtures/TF001/positive.tf +24 -0
- package/src/lint/post-synth/fixtures/TF002/negative.tf +17 -0
- package/src/lint/post-synth/fixtures/TF002/positive-legacy.tf +10 -0
- package/src/lint/post-synth/fixtures/TF002/positive-no-version.tf +12 -0
- package/src/lint/post-synth/fixtures/TF002/positive.tf +7 -0
- package/src/lint/post-synth/fixtures/TF003/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF003/positive.tf +8 -0
- package/src/lint/post-synth/fixtures/TF004/negative-git.tf +3 -0
- package/src/lint/post-synth/fixtures/TF004/negative-local.tf +3 -0
- package/src/lint/post-synth/fixtures/TF004/negative.tf +4 -0
- package/src/lint/post-synth/fixtures/TF004/positive.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative-local.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative-registry.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative-sha.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/positive-branch.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/positive-non-semver.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/positive.tf +3 -0
- package/src/lint/post-synth/fixtures/TF006/negative.tf +5 -0
- package/src/lint/post-synth/fixtures/TF006/positive-empty-default.tf +11 -0
- package/src/lint/post-synth/fixtures/TF006/positive.tf +6 -0
- package/src/lint/post-synth/fixtures/TF007/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF007/positive.tf +9 -0
- package/src/lint/post-synth/fixtures/TF008/negative-variable.tf +10 -0
- package/src/lint/post-synth/fixtures/TF008/negative.tf +8 -0
- package/src/lint/post-synth/fixtures/TF008/positive-other-provider.tf +10 -0
- package/src/lint/post-synth/fixtures/TF008/positive.tf +5 -0
- package/src/lint/post-synth/fixtures/TF009/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF009/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF010/negative.tf +5 -0
- package/src/lint/post-synth/fixtures/TF010/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF011/negative.tf +5 -0
- package/src/lint/post-synth/fixtures/TF011/positive-blank.tf +5 -0
- package/src/lint/post-synth/fixtures/TF011/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF012/negative.tf +4 -0
- package/src/lint/post-synth/fixtures/TF012/positive.tf +3 -0
- package/src/lint/post-synth/fixtures/TF013/negative.tf +8 -0
- package/src/lint/post-synth/fixtures/TF013/positive-data.tf +8 -0
- package/src/lint/post-synth/fixtures/TF013/positive.tf +8 -0
- package/src/lint/post-synth/fixtures/TF014/negative/main.tf +26 -0
- package/src/lint/post-synth/fixtures/TF014/negative/modules/cdn/main.tf +19 -0
- package/src/lint/post-synth/fixtures/TF014/positive/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF014/positive/modules/cdn/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF015/negative/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF015/negative/modules/cdn/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF015/positive/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF015/positive/modules/cdn/main.tf +14 -0
- package/src/lint/post-synth/fixtures/TF016/negative-template.tf +7 -0
- package/src/lint/post-synth/fixtures/TF016/negative.tf +16 -0
- package/src/lint/post-synth/fixtures/TF016/positive-shared-file.tf +12 -0
- package/src/lint/post-synth/fixtures/TF016/positive.tf +13 -0
- package/src/lint/post-synth/fixtures/TF017/negative.tf +4 -0
- package/src/lint/post-synth/fixtures/TF017/positive.tf +5 -0
- package/src/lint/post-synth/fixtures/TF018/negative.tf +9 -0
- package/src/lint/post-synth/fixtures/TF018/positive-data.tf +9 -0
- package/src/lint/post-synth/fixtures/TF018/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF019/negative.tf +14 -0
- package/src/lint/post-synth/fixtures/TF019/positive-output.tf +5 -0
- package/src/lint/post-synth/fixtures/TF019/positive.tf +16 -0
- package/src/lint/post-synth/fixtures/TF020/negative.tf +43 -0
- package/src/lint/post-synth/fixtures/TF020/positive.tf +32 -0
- package/src/lint/post-synth/fixtures/TF020/scoped/main.tf +21 -0
- package/src/lint/post-synth/fixtures/TF020/scoped/modules/cdn/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF021/negative.tf +31 -0
- package/src/lint/post-synth/fixtures/TF021/positive-length.tf +8 -0
- package/src/lint/post-synth/fixtures/TF021/positive.tf +8 -0
- package/src/lint/post-synth/fixtures/TF022/negative-locator.tf +7 -0
- package/src/lint/post-synth/fixtures/TF022/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF022/positive-nested.tf +10 -0
- package/src/lint/post-synth/fixtures/TF022/positive.tf +6 -0
- package/src/lint/post-synth/fixtures/TF023/negative/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF023/positive/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF023/positive/state.json +24 -0
- package/src/lint/post-synth/fixtures/TF024/live-clean/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF024/live-with-backend/main.tf +14 -0
- package/src/lint/post-synth/fixtures/TF024/live-with-cloud/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF025/live-clean/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF025/live-with-workspace-ref/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF026/live-default/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF026/live-explicit-delete/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF026/live-keep/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF027/live-default/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF027/live-untagged-delete/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF027/live-untagged-delete-scoped/main.tf +17 -0
- package/src/lint/post-synth/fixtures/TF027/live-untagged-report/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF028/live/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF028/stock/main.tf +10 -0
- package/src/lint/post-synth/fixtures/load.ts +108 -0
- package/src/lint/post-synth/index.ts +52 -0
- package/src/lint/post-synth/live-refusals-config-format.test.ts +143 -0
- package/src/lint/post-synth/module-rules.test.ts +153 -0
- package/src/lint/post-synth/module-source.test.ts +75 -0
- package/src/lint/post-synth/module-source.ts +121 -0
- package/src/lint/post-synth/post-synth.test.ts +73 -16
- package/src/lint/post-synth/scope.ts +107 -0
- package/src/lint/post-synth/single-block.test.ts +263 -0
- package/src/lint/post-synth/suppression.test.ts +110 -0
- package/src/lint/post-synth/tf001.ts +98 -18
- package/src/lint/post-synth/tf002.test.ts +93 -0
- package/src/lint/post-synth/tf002.ts +151 -0
- package/src/lint/post-synth/tf003.test.ts +75 -0
- package/src/lint/post-synth/tf003.ts +71 -0
- package/src/lint/post-synth/tf004.test.ts +47 -0
- package/src/lint/post-synth/tf004.ts +63 -0
- package/src/lint/post-synth/tf005.test.ts +57 -0
- package/src/lint/post-synth/tf005.ts +76 -0
- package/src/lint/post-synth/tf006.ts +53 -0
- package/src/lint/post-synth/tf007.ts +78 -0
- package/src/lint/post-synth/tf008.ts +75 -0
- package/src/lint/post-synth/tf009.ts +59 -0
- package/src/lint/post-synth/tf010.ts +51 -0
- package/src/lint/post-synth/tf011.ts +49 -0
- package/src/lint/post-synth/tf012.ts +47 -0
- package/src/lint/post-synth/tf013.ts +63 -0
- package/src/lint/post-synth/tf014.ts +72 -0
- package/src/lint/post-synth/tf015.ts +72 -0
- package/src/lint/post-synth/tf016.ts +75 -0
- package/src/lint/post-synth/tf017.ts +50 -0
- package/src/lint/post-synth/tf018.ts +72 -0
- package/src/lint/post-synth/tf019.ts +72 -0
- package/src/lint/post-synth/tf020.ts +123 -0
- package/src/lint/post-synth/tf021.ts +126 -0
- package/src/lint/post-synth/tf022.ts +61 -0
- package/src/lint/post-synth/tf024.test.ts +42 -0
- package/src/lint/post-synth/tf024.ts +70 -0
- package/src/lint/post-synth/tf025.test.ts +46 -0
- package/src/lint/post-synth/tf025.ts +101 -0
- package/src/lint/post-synth/tf026.test.ts +56 -0
- package/src/lint/post-synth/tf026.ts +84 -0
- package/src/lint/post-synth/tf027.test.ts +62 -0
- package/src/lint/post-synth/tf027.ts +74 -0
- package/src/lint/post-synth/tf028.test.ts +92 -0
- package/src/lint/post-synth/tf028.ts +96 -0
- package/src/lint/presets.test.ts +72 -0
- package/src/lint/rules/fixtures/TF101/negative-op.ts +2 -0
- package/src/lint/rules/fixtures/TF101/positive-op.ts +2 -0
- package/src/lint/rules/fixtures/load.ts +53 -0
- package/src/lint/rules/plan-before-apply.test.ts +112 -7
- package/src/lint/rules/plan-before-apply.ts +11 -0
- package/src/lint/secret-shape.test.ts +150 -0
- package/src/lint/secret-shape.ts +159 -0
- package/src/live-ownership.ts +60 -0
- package/src/lsp/completions.test.ts +3 -2
- package/src/lsp/option-keys.ts +12 -6
- package/src/op/activities/choudoufu.acceptance.test.ts +127 -0
- package/src/op/activities/choudoufu.test.ts +725 -0
- package/src/op/activities/index.ts +40 -4
- package/src/op/activities/live-detect.test.ts +143 -0
- package/src/op/activities/live-detect.ts +162 -0
- package/src/op/activities/registry.test.ts +11 -3
- package/src/op/activities/terraform.test.ts +7 -3
- package/src/op/activities/terraform.ts +861 -69
- package/src/op/activity-contracts.test.ts +147 -0
- package/src/op/activity-contracts.ts +178 -0
- package/src/op/adoption.test.ts +170 -0
- package/src/op/adoption.ts +241 -0
- package/src/op/builders.test.ts +60 -5
- package/src/op/builders.ts +95 -5
- package/src/op/resolve-root-mode.test.ts +85 -0
- package/src/op/resolve-root-mode.ts +86 -0
- package/src/plugin.test.ts +106 -0
- package/src/plugin.ts +186 -29
- package/src/skills/chant-terraform.md +1 -1
- package/src/validate.ts +10 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TF025: a live root reads a non-default `terraform.workspace`, or configures
|
|
3
|
+
* one through `terraform.roots`.
|
|
4
|
+
*
|
|
5
|
+
* choudoufu refuses any workspace but `default` on a live root, and refuses
|
|
6
|
+
* `workspace new`/`workspace select` outright: only the default workspace
|
|
7
|
+
* exists there ("How you run it" in choudoufu's compatibility reference).
|
|
8
|
+
* Two independent ways a configuration ends up asking for one:
|
|
9
|
+
*
|
|
10
|
+
* - `terraform.roots.<name>.workspace` in `chant.config.*` selects a
|
|
11
|
+
* workspace via `TF_WORKSPACE` before any HCL is even read, chant's own
|
|
12
|
+
* equivalent of `workspace select`, so it is flagged the same way.
|
|
13
|
+
* - A `"${terraform.workspace}"` interpolation anywhere in a block's body
|
|
14
|
+
* reads the (necessarily `"default"`) workspace name in configuration,
|
|
15
|
+
* which is not itself refused by choudoufu but is dead code on a live
|
|
16
|
+
* root and a sign the configuration was written for a workspace-per-
|
|
17
|
+
* environment layout that live mode does not support.
|
|
18
|
+
*
|
|
19
|
+
* The first fires once per root (it names a root-level config field, not a
|
|
20
|
+
* particular block); the second fires once per block that contains the
|
|
21
|
+
* reference, since each is its own thing to fix.
|
|
22
|
+
*
|
|
23
|
+
* Scope: the config half is root-only, since it reads a root-level config
|
|
24
|
+
* field; the reference half reads any block of a live root, a descended child
|
|
25
|
+
* module's included, and names the call chain that reached it (#2112).
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type {
|
|
29
|
+
PostSynthCheck,
|
|
30
|
+
PostSynthContext,
|
|
31
|
+
PostSynthDiagnostic,
|
|
32
|
+
} from "@intentius/chant/lint/post-synth";
|
|
33
|
+
import { isResourceDeclarable } from "@intentius/chant/declarable";
|
|
34
|
+
import { isRootScoped } from "./scope";
|
|
35
|
+
|
|
36
|
+
/** Recursively search a parsed HCL body for a `terraform.workspace` reference, un-evaluated interpolations included. */
|
|
37
|
+
function containsWorkspaceRef(value: unknown): boolean {
|
|
38
|
+
if (typeof value === "string") return /\bterraform\.workspace\b/.test(value);
|
|
39
|
+
if (Array.isArray(value)) return value.some(containsWorkspaceRef);
|
|
40
|
+
if (value !== null && typeof value === "object") return Object.values(value).some(containsWorkspaceRef);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const tf025: PostSynthCheck = {
|
|
45
|
+
id: "TF025",
|
|
46
|
+
description: "Live root references a non-default terraform.workspace, which choudoufu refuses",
|
|
47
|
+
|
|
48
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
49
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
50
|
+
const flaggedRootWorkspace = new Set<string>();
|
|
51
|
+
|
|
52
|
+
for (const [name, entity] of ctx.entities) {
|
|
53
|
+
if (!isResourceDeclarable(entity)) continue;
|
|
54
|
+
const props = entity.props as {
|
|
55
|
+
root?: unknown;
|
|
56
|
+
body?: unknown;
|
|
57
|
+
mode?: unknown;
|
|
58
|
+
workspace?: unknown;
|
|
59
|
+
address?: unknown;
|
|
60
|
+
};
|
|
61
|
+
if (props.mode !== "live") continue;
|
|
62
|
+
const root = typeof props.root === "string" ? props.root : "";
|
|
63
|
+
|
|
64
|
+
if (
|
|
65
|
+
typeof props.workspace === "string" &&
|
|
66
|
+
props.workspace !== "" &&
|
|
67
|
+
props.workspace !== "default" &&
|
|
68
|
+
isRootScoped(entity) &&
|
|
69
|
+
!flaggedRootWorkspace.has(root)
|
|
70
|
+
) {
|
|
71
|
+
flaggedRootWorkspace.add(root);
|
|
72
|
+
diagnostics.push({
|
|
73
|
+
checkId: "TF025",
|
|
74
|
+
severity: "error",
|
|
75
|
+
message:
|
|
76
|
+
`Root module "${root}" runs choudoufu with a declared estate and configures workspace ` +
|
|
77
|
+
`"${props.workspace}" in terraform.roots.${root}. choudoufu refuses any workspace but ` +
|
|
78
|
+
'"default" on a live root. Remove `workspace` from this root\'s config (or set it to "default").',
|
|
79
|
+
entity: name,
|
|
80
|
+
lexicon: "terraform",
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (containsWorkspaceRef(props.body)) {
|
|
85
|
+
const address = typeof props.address === "string" ? props.address : name;
|
|
86
|
+
diagnostics.push({
|
|
87
|
+
checkId: "TF025",
|
|
88
|
+
severity: "error",
|
|
89
|
+
message:
|
|
90
|
+
`"${address}" in root module "${root}" references \`terraform.workspace\`. choudoufu refuses ` +
|
|
91
|
+
'any workspace but "default" on a live root, so this always resolves to "default" there. ' +
|
|
92
|
+
"Remove the reference.",
|
|
93
|
+
entity: name,
|
|
94
|
+
lexicon: "terraform",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return diagnostics;
|
|
100
|
+
},
|
|
101
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TF026: a live root declares `delete: "never"` but its `policy` block still
|
|
3
|
+
* deletes an orphaned marked resource.
|
|
4
|
+
*
|
|
5
|
+
* chant's cross-lexicon delete-mode vocabulary (`terraform.roots.<name>.delete`,
|
|
6
|
+
* `../../config.ts`'s `TerraformDeleteMode`) maps onto choudoufu's `policy`
|
|
7
|
+
* block: `undeclared_tagged` governs what happens to a resource this estate
|
|
8
|
+
* marked that the configuration no longer declares, and defaults to
|
|
9
|
+
* `"delete"` when the `policy` block omits it, or the root declares no
|
|
10
|
+
* `policy` block at all. `delete: "never"` promises that `TerraformApplyOp`
|
|
11
|
+
* never proposes deleting a resource it owns, so a root making that promise
|
|
12
|
+
* has to have actually turned the quadrant's default off — `"keep"`,
|
|
13
|
+
* `"untag"` or `"report"` all satisfy it; anything else (the unset default,
|
|
14
|
+
* or an explicit `"delete"`) does not.
|
|
15
|
+
*
|
|
16
|
+
* One diagnostic per root, fired from the root's `Terraform::Live` entity —
|
|
17
|
+
* the same entity TF024 and TF025 fire from.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type {
|
|
21
|
+
PostSynthCheck,
|
|
22
|
+
PostSynthContext,
|
|
23
|
+
PostSynthDiagnostic,
|
|
24
|
+
} from "@intentius/chant/lint/post-synth";
|
|
25
|
+
import { isResourceDeclarable } from "@intentius/chant/declarable";
|
|
26
|
+
import { LIVE_TYPE, type BlockBody } from "../../hcl/parse";
|
|
27
|
+
|
|
28
|
+
/** Verbs that satisfy `delete: "never"` for the `undeclared_tagged` quadrant. */
|
|
29
|
+
const SATISFIES_NEVER = new Set(["keep", "untag", "report"]);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The `policy { }` block nested in a `live { }` block's body, hcl2json's own
|
|
33
|
+
* "one block, wrapped in an array" shape. Exported for TF027 (`./tf027.ts`),
|
|
34
|
+
* which reads the other quadrant of the same block.
|
|
35
|
+
*/
|
|
36
|
+
export function policyBlockOf(liveBody: BlockBody): BlockBody | undefined {
|
|
37
|
+
const policy = liveBody["policy"];
|
|
38
|
+
return Array.isArray(policy) && policy.length > 0 && typeof policy[0] === "object" && policy[0] !== null
|
|
39
|
+
? (policy[0] as BlockBody)
|
|
40
|
+
: undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const tf026: PostSynthCheck = {
|
|
44
|
+
id: "TF026",
|
|
45
|
+
description: "Live root declares delete: \"never\" but its policy leaves undeclared_tagged at \"delete\"",
|
|
46
|
+
|
|
47
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
48
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
49
|
+
const flagged = new Set<string>();
|
|
50
|
+
|
|
51
|
+
for (const [name, entity] of ctx.entities) {
|
|
52
|
+
if (entity.entityType !== LIVE_TYPE) continue;
|
|
53
|
+
if (!isResourceDeclarable(entity)) continue;
|
|
54
|
+
const props = entity.props as { root?: unknown; body?: unknown; mode?: unknown; delete?: unknown };
|
|
55
|
+
if (props.mode !== "live") continue;
|
|
56
|
+
if (props.delete !== "never") continue;
|
|
57
|
+
const root = typeof props.root === "string" ? props.root : "";
|
|
58
|
+
if (flagged.has(root)) continue;
|
|
59
|
+
|
|
60
|
+
const body = (typeof props.body === "object" && props.body !== null ? props.body : {}) as BlockBody;
|
|
61
|
+
const policy = policyBlockOf(body);
|
|
62
|
+
const undeclaredTagged = policy && typeof policy["undeclared_tagged"] === "string" ? (policy["undeclared_tagged"] as string) : undefined;
|
|
63
|
+
if (undeclaredTagged !== undefined && SATISFIES_NEVER.has(undeclaredTagged)) continue;
|
|
64
|
+
|
|
65
|
+
flagged.add(root);
|
|
66
|
+
const found =
|
|
67
|
+
undeclaredTagged === undefined
|
|
68
|
+
? "leaves undeclared_tagged unset, which defaults to \"delete\""
|
|
69
|
+
: `sets undeclared_tagged = "${undeclaredTagged}"`;
|
|
70
|
+
diagnostics.push({
|
|
71
|
+
checkId: "TF026",
|
|
72
|
+
severity: "error",
|
|
73
|
+
message:
|
|
74
|
+
`Root module "${root}" declares delete: "never" (terraform.roots.${root}.delete), but its ` +
|
|
75
|
+
`policy block ${found}. Add undeclared_tagged = "keep" (or "untag" or "report") to the live ` +
|
|
76
|
+
"block's policy block, or change this root's delete mode.",
|
|
77
|
+
entity: name,
|
|
78
|
+
lexicon: "terraform",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return diagnostics;
|
|
83
|
+
},
|
|
84
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TF027: a live root's `policy` block sets `undeclared_untagged = "delete"`.
|
|
3
|
+
*
|
|
4
|
+
* The `undeclared_untagged` quadrant covers a live resource this estate never
|
|
5
|
+
* marked and the configuration does not declare, a resource chant does not
|
|
6
|
+
* own. `"delete"` there is account-scoped reconciliation: choudoufu compares
|
|
7
|
+
* the configuration against everything a `scope` block reaches and removes
|
|
8
|
+
* what is not in it. chant never proposes deleting a resource it does not
|
|
9
|
+
* own, and neither does an Op it generates, so the setting is refused on any
|
|
10
|
+
* live root the project builds, scoped or not. Narrowing the estate's own
|
|
11
|
+
* ownership answer (`undeclared_tagged`, which TF026 checks against
|
|
12
|
+
* `delete: "never"`) is the setting to reach for instead.
|
|
13
|
+
*
|
|
14
|
+
* `TerraformApplyOp` (`../../composites/terraform-apply-op.ts`) makes the
|
|
15
|
+
* same refusal when it builds, but only for a project on a `chant.config.json`
|
|
16
|
+
* whose root it can resolve synchronously (#2216): a `chant.config.ts` is
|
|
17
|
+
* project-authored code that `resolveRootModeSync` will not evaluate, so the
|
|
18
|
+
* composite reads the root as stock and never asks the policy question. This
|
|
19
|
+
* check reads `props.mode` off the parsed HCL instead, which the build stamps
|
|
20
|
+
* on every entity whatever the config file is written in, so it is the
|
|
21
|
+
* refusal that actually fires. It also fires for a live root with no apply Op
|
|
22
|
+
* at all, since the setting is a statement about the estate rather than about
|
|
23
|
+
* one Op.
|
|
24
|
+
*
|
|
25
|
+
* One diagnostic per root, fired from the root's `Terraform::Live` entity,
|
|
26
|
+
* the same entity TF024, TF025 and TF026 fire from.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import type {
|
|
30
|
+
PostSynthCheck,
|
|
31
|
+
PostSynthContext,
|
|
32
|
+
PostSynthDiagnostic,
|
|
33
|
+
} from "@intentius/chant/lint/post-synth";
|
|
34
|
+
import { isResourceDeclarable } from "@intentius/chant/declarable";
|
|
35
|
+
import { LIVE_TYPE, type BlockBody } from "../../hcl/parse";
|
|
36
|
+
import { policyBlockOf } from "./tf026";
|
|
37
|
+
|
|
38
|
+
export const tf027: PostSynthCheck = {
|
|
39
|
+
id: "TF027",
|
|
40
|
+
description: "Live root's policy block sets undeclared_untagged = \"delete\", which deletes resources chant does not own",
|
|
41
|
+
|
|
42
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
43
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
44
|
+
const flagged = new Set<string>();
|
|
45
|
+
|
|
46
|
+
for (const [name, entity] of ctx.entities) {
|
|
47
|
+
if (entity.entityType !== LIVE_TYPE) continue;
|
|
48
|
+
if (!isResourceDeclarable(entity)) continue;
|
|
49
|
+
const props = entity.props as { root?: unknown; body?: unknown; mode?: unknown };
|
|
50
|
+
if (props.mode !== "live") continue;
|
|
51
|
+
const root = typeof props.root === "string" ? props.root : "";
|
|
52
|
+
if (flagged.has(root)) continue;
|
|
53
|
+
|
|
54
|
+
const body = (typeof props.body === "object" && props.body !== null ? props.body : {}) as BlockBody;
|
|
55
|
+
const policy = policyBlockOf(body);
|
|
56
|
+
if (!policy || policy["undeclared_untagged"] !== "delete") continue;
|
|
57
|
+
|
|
58
|
+
flagged.add(root);
|
|
59
|
+
diagnostics.push({
|
|
60
|
+
checkId: "TF027",
|
|
61
|
+
severity: "error",
|
|
62
|
+
message:
|
|
63
|
+
`Root module "${root}"'s policy block sets undeclared_untagged = "delete" (account-scoped ` +
|
|
64
|
+
"reconciliation, scoped by a `scope` block). chant never proposes deleting a resource it does " +
|
|
65
|
+
"not own, and neither does an Op it generates. Remove that setting from the policy block, or " +
|
|
66
|
+
"narrow the estate's ownership answer (undeclared_tagged) instead of the account's.",
|
|
67
|
+
entity: name,
|
|
68
|
+
lexicon: "terraform",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return diagnostics;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TF028: a live root is watched by a `TerraformWatchOp` built in stock mode.
|
|
3
|
+
*
|
|
4
|
+
* `TerraformWatchOp`'s `live` flag is declared, not detected: a composite is
|
|
5
|
+
* built by `chant build` without reading a root module's `.tf` files, so the
|
|
6
|
+
* Op author says which shape the Plan phase takes and, until #2216, nothing
|
|
7
|
+
* checked the answer against the root. Leaving it off a live root is silent
|
|
8
|
+
* and lossy. `live: true` swaps the Plan step for `choudoufuLivePlan`, whose
|
|
9
|
+
* single read of the live system answers three questions (drift, how many
|
|
10
|
+
* live resources sit at a declared identity carrying no marker, and how many
|
|
11
|
+
* of those an exact content match makes claimable); a stock `terraformPlan`
|
|
12
|
+
* step answers the first alone, so the watch reports drift and stays quiet
|
|
13
|
+
* about every unowned and adoptable resource in the estate.
|
|
14
|
+
*
|
|
15
|
+
* The check runs over the whole built graph because that is where both halves
|
|
16
|
+
* of the question are: the root's mode is stamped on every parsed entity by
|
|
17
|
+
* the build (`props.mode`, #2103), and the Op is a `Chant::Op` entity carrying
|
|
18
|
+
* the composite's own labels. `TerraformWatchOp` itself cross-checks the flag
|
|
19
|
+
* where it can (`../../op/resolve-root-mode.ts`), but that read is a
|
|
20
|
+
* `chant.config.json`-only one, so on a `chant.config.ts` project, which is
|
|
21
|
+
* every project in this repository, this check is what actually fires.
|
|
22
|
+
*
|
|
23
|
+
* Read off the labels the composite stamps rather than off the emitted step,
|
|
24
|
+
* since a stock `terraformPlan -out` step against a live root is correct in
|
|
25
|
+
* `TerraformApplyOp`: choudoufu v0.13.0 accepts `plan -out` and re-plans
|
|
26
|
+
* against the file at apply (#2157), so the apply Op builds one shape for
|
|
27
|
+
* both kinds of root and the step alone says nothing about a mistake.
|
|
28
|
+
*
|
|
29
|
+
* One diagnostic per Op, fired from the Op's entity.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import type {
|
|
33
|
+
PostSynthCheck,
|
|
34
|
+
PostSynthContext,
|
|
35
|
+
PostSynthDiagnostic,
|
|
36
|
+
} from "@intentius/chant/lint/post-synth";
|
|
37
|
+
import { isResourceDeclarable } from "@intentius/chant/declarable";
|
|
38
|
+
import { isOpEntity } from "@intentius/chant/op/resource";
|
|
39
|
+
|
|
40
|
+
/** The labels `TerraformWatchOp` stamps on the Op it builds (`../../composites/terraform-watch-op.ts`). */
|
|
41
|
+
interface WatchOpLabels {
|
|
42
|
+
Watch?: unknown;
|
|
43
|
+
TerraformRoot?: unknown;
|
|
44
|
+
TerraformMode?: unknown;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Every root name the build parsed as live: `terraform.binary: "choudoufu"` plus a declared estate. */
|
|
48
|
+
function liveRootsOf(ctx: PostSynthContext): Set<string> {
|
|
49
|
+
const roots = new Set<string>();
|
|
50
|
+
for (const entity of ctx.entities.values()) {
|
|
51
|
+
if (!isResourceDeclarable(entity)) continue;
|
|
52
|
+
const props = entity.props as { root?: unknown; mode?: unknown };
|
|
53
|
+
if (props.mode !== "live" || typeof props.root !== "string") continue;
|
|
54
|
+
roots.add(props.root);
|
|
55
|
+
}
|
|
56
|
+
return roots;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const tf028: PostSynthCheck = {
|
|
60
|
+
id: "TF028",
|
|
61
|
+
description: "A live root is watched by a TerraformWatchOp built without live: true",
|
|
62
|
+
|
|
63
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
64
|
+
const liveRoots = liveRootsOf(ctx);
|
|
65
|
+
if (liveRoots.size === 0) return [];
|
|
66
|
+
|
|
67
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
68
|
+
|
|
69
|
+
for (const [name, entity] of ctx.entities) {
|
|
70
|
+
if (!isOpEntity(entity)) continue;
|
|
71
|
+
const props = (entity as { props?: Record<string, unknown> }).props;
|
|
72
|
+
if (!props || typeof props.name !== "string") continue;
|
|
73
|
+
const labels = (typeof props.labels === "object" && props.labels !== null
|
|
74
|
+
? props.labels
|
|
75
|
+
: {}) as WatchOpLabels;
|
|
76
|
+
if (labels.Watch !== "true") continue;
|
|
77
|
+
if (labels.TerraformMode === "live") continue;
|
|
78
|
+
const root = labels.TerraformRoot;
|
|
79
|
+
if (typeof root !== "string" || !liveRoots.has(root)) continue;
|
|
80
|
+
|
|
81
|
+
diagnostics.push({
|
|
82
|
+
checkId: "TF028",
|
|
83
|
+
severity: "error",
|
|
84
|
+
message:
|
|
85
|
+
`Op "${props.name}" watches root module "${root}" in stock mode, but that root runs choudoufu ` +
|
|
86
|
+
"with a declared estate. A stock plan reports drift alone and stays quiet about every unowned " +
|
|
87
|
+
`and adoptable resource in the estate. Set live: true on this TerraformWatchOp, or drop the ` +
|
|
88
|
+
"root's estate if it is not a live root.",
|
|
89
|
+
entity: name,
|
|
90
|
+
lexicon: "terraform",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return diagnostics;
|
|
95
|
+
},
|
|
96
|
+
};
|
|
@@ -65,7 +65,7 @@ export const { op } = TerraformApplyOp({
|
|
|
65
65
|
});
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
`gate: "never"`
|
|
68
|
+
`gate: "never"` drops the Gate phase, so `chant run` walks straight from Plan to Apply; any other mode emits a Gate phase, and a run that reaches an unapproved gate records a pending fact on the gate ledger, ends `gated` and exits 3 until someone runs `chant approve <op> <gate>`. The Gate phase always shows the saved plan first and reports its `destroys` count as a `Destroys` run outcome, because `GateStep` carries no condition to branch on at build time — the approver sees what's at stake before approving, rather than the Op deciding for them.
|
|
69
69
|
|
|
70
70
|
Terraform has no automatic rollback, so `compensate: true` with no command throws at build time, naming the Op, rather than warning once an apply has already half-run:
|
|
71
71
|
|
package/dist/validate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAElG,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAElG,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAcvF;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAQpF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-terraform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src/",
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
"types": "./dist/op/activities/index.d.ts",
|
|
23
23
|
"default": "./src/op/activities/index.ts"
|
|
24
24
|
},
|
|
25
|
+
"./op/activity-contracts": {
|
|
26
|
+
"development": "./src/op/activity-contracts.ts",
|
|
27
|
+
"types": "./dist/op/activity-contracts.d.ts",
|
|
28
|
+
"default": "./src/op/activity-contracts.ts"
|
|
29
|
+
},
|
|
25
30
|
"./lint/post-synth": {
|
|
26
31
|
"development": "./src/lint/post-synth/index.ts",
|
|
27
32
|
"types": "./dist/lint/post-synth/index.d.ts",
|
|
@@ -51,7 +56,7 @@
|
|
|
51
56
|
},
|
|
52
57
|
"peerDependencies": {
|
|
53
58
|
"zod": "^4.3.6",
|
|
54
|
-
"@intentius/chant": "^0.
|
|
59
|
+
"@intentius/chant": "^0.59.0",
|
|
55
60
|
"typescript": "^5.9.3"
|
|
56
61
|
},
|
|
57
62
|
"description": "terraform lexicon for chant, existing Terraform root modules as declarable entities",
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# What the gated acceptance suites have actually run against
|
|
2
|
+
|
|
3
|
+
Four `describe` blocks across three files run this lexicon against a real
|
|
4
|
+
binary rather than a fixture, and every one of them skips rather than fails
|
|
5
|
+
when its dependency is absent. A skip is silent by design, so without this
|
|
6
|
+
file nothing in the tree says which of them has ever passed, against what, or
|
|
7
|
+
when. The evidence had been living in PR prose (`#2157` for the live apply
|
|
8
|
+
pair), which no reader of the suite can see.
|
|
9
|
+
|
|
10
|
+
One row per gated block below: what it proves, what gates it, the command,
|
|
11
|
+
and the last binary and emulator it is known to have passed against.
|
|
12
|
+
|
|
13
|
+
`live-estate/README.md` next door is the sibling record for the two recorded
|
|
14
|
+
documents (`../live-plan.json`, `../live-ls.json`) rather than for a suite,
|
|
15
|
+
and stays where it is.
|
|
16
|
+
|
|
17
|
+
## The binary and the emulator these rows name
|
|
18
|
+
|
|
19
|
+
`choudoufu v0.14.0` below means the published release binary, not a source
|
|
20
|
+
build: `gh release download v0.14.0 -R INTENTIUS/choudoufu -p
|
|
21
|
+
'choudoufu_v0.14.0_darwin_arm64.tar.gz' -p SHA256SUMS`, verified against
|
|
22
|
+
`SHA256SUMS` (`41c705d9b5fec47100c4f2fb9ab0694f0160b31a4e0661e877b4d9821bc464e3`),
|
|
23
|
+
extracted and put first on PATH. `choudoufu version` prints `choudoufu v0.14.0
|
|
24
|
+
(based on OpenTofu v1.13.0-dev)`.
|
|
25
|
+
|
|
26
|
+
`the pinned floci emulator` means choudoufu's own smoke stack image,
|
|
27
|
+
`ghcr.io/lex00/floci@sha256:a39185cc3971d0188663d61043cb038dff1260d8a975b1aa72c4e2bb1feac3cb`
|
|
28
|
+
(that checkout's `live/floci-image`), brought up from
|
|
29
|
+
`live/smoke/docker-compose.yml` and exported as
|
|
30
|
+
`CHOUDOUFU_EMULATOR_ENDPOINT=http://localhost:<mapped port>`.
|
|
31
|
+
|
|
32
|
+
## The four blocks
|
|
33
|
+
|
|
34
|
+
### 1. `TerraformApplyOp applies a real root`
|
|
35
|
+
|
|
36
|
+
`composites/terraform-apply-op.acceptance.test.ts`. One test: `runOpLocally`
|
|
37
|
+
drives `TerraformApplyOp` with `gate: "never"` through Init, Plan and Apply
|
|
38
|
+
over the `with-backend/` fixture in a temp directory, and two
|
|
39
|
+
`null_resource`s in the resulting state is the pass. The backend is `local`,
|
|
40
|
+
so no credentials and no remote state are involved.
|
|
41
|
+
|
|
42
|
+
Gated on a `terraform` or `tofu` on PATH, and on the provider download
|
|
43
|
+
reaching `registry.terraform.io` (`CHANT_OFFLINE` unset and the host
|
|
44
|
+
resolvable).
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
npx vitest run lexicons/terraform/src/composites/terraform-apply-op.acceptance.test.ts
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Last passed: 2026-09-07, `Terraform v1.15.8` on `darwin_arm64`, with
|
|
51
|
+
`hashicorp/null` resolved from the public registry for the fixture's `~> 3.2`
|
|
52
|
+
constraint. No emulator is involved. Re-run by chant #2168 alongside the two
|
|
53
|
+
live blocks in the same file, so the file's three rows all come from one run.
|
|
54
|
+
|
|
55
|
+
### 2. `TerraformApplyOp applies a live root against choudoufu's emulator`
|
|
56
|
+
|
|
57
|
+
Same file, second block. Two tests. The first is the live-root happy path end
|
|
58
|
+
to end through `runOpLocally`: Init, `plan -out=<file>`, `show` over that
|
|
59
|
+
file, `apply <file>`, two `null_resource`s applied against the `live/`
|
|
60
|
+
fixture. The second moves the world between plan and apply and asserts both
|
|
61
|
+
named refusals (`approval-mismatch`, `wrong-estate`) come back as results
|
|
62
|
+
carrying choudoufu's own message rather than as thrown errors, then applies
|
|
63
|
+
the same file once the world is restored.
|
|
64
|
+
|
|
65
|
+
Gated on a `choudoufu` on PATH, on it reporting a release version at or above
|
|
66
|
+
`MIN_CHOUDOUFU_VERSION` (0.14.0), and on `CHOUDOUFU_EMULATOR_ENDPOINT`. Not
|
|
67
|
+
gated on choudoufu #894 and never was: the plan half here is the stock `plan
|
|
68
|
+
-out` path and reads no JSON document.
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
npx vitest run lexicons/terraform/src/composites/terraform-apply-op.acceptance.test.ts
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Last passed: 2026-09-07, choudoufu v0.14.0 with the pinned floci emulator up,
|
|
75
|
+
recorded by chant #2168. That run is reproducible from this file: the binary
|
|
76
|
+
is a release asset and the emulator is a pinned digest. Before it, the only
|
|
77
|
+
record was PR #2157's prose ("the acceptance suite 3 passed against v0.13.0
|
|
78
|
+
with the emulator up", merged at `81200b5d97c15920619de4b109c4393ec3223720`),
|
|
79
|
+
which #2220 could not reproduce because no binary existed on the machine that
|
|
80
|
+
wrote this file.
|
|
81
|
+
|
|
82
|
+
### 3. `choudoufu live-check and live-plan against the fixture`
|
|
83
|
+
|
|
84
|
+
`op/activities/choudoufu.acceptance.test.ts`. Two tests. `live-check -json`
|
|
85
|
+
admits the `live/` fixture root (no cloud calls, so this half needs only the
|
|
86
|
+
binary), and `live-plan` reads the emulator and proposes creating both
|
|
87
|
+
`null_resource`s on a fresh estate.
|
|
88
|
+
|
|
89
|
+
Gated on a `choudoufu` on PATH and on `CHOUDOUFU_EMULATOR_ENDPOINT`. The
|
|
90
|
+
`CHOUDOUFU_894_OPEN` constant that used to skip the block whatever was on
|
|
91
|
+
PATH is gone.
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
npx vitest run lexicons/terraform/src/op/activities/choudoufu.acceptance.test.ts
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Last passed: 2026-09-07, choudoufu v0.14.0 with the pinned floci emulator up,
|
|
98
|
+
recorded by chant #2168. That is the first pass this block has ever had as a
|
|
99
|
+
suite, and the first the `live-plan` test has ever had at all. It had been
|
|
100
|
+
gated shut on choudoufu #894 since commit `65821d76` (2026-09-06 12:45Z);
|
|
101
|
+
before that gate, PR #2135's body records a hand verification of the
|
|
102
|
+
`live-check` half only.
|
|
103
|
+
|
|
104
|
+
Two chant-side changes in #2168 were what the pass needed, both consequences
|
|
105
|
+
of what choudoufu PR 915 shipped. `-estate` is still refused beside a declared
|
|
106
|
+
estate; what changed is that the flag is no longer needed, so
|
|
107
|
+
`choudoufuLivePlanCommand` omits it exactly when the configuration names its
|
|
108
|
+
own estate. And the human-render call carries `-detailed-exitcode` too, so
|
|
109
|
+
exit 2 there is a plan with changes rather than a failure, which is what the
|
|
110
|
+
first real run of this block found.
|
|
111
|
+
|
|
112
|
+
### 4. `TerraformAdoptOp adopts an unmarked live resource`
|
|
113
|
+
|
|
114
|
+
`composites/terraform-adopt-op.acceptance.test.ts`. One test: create an
|
|
115
|
+
unmarked VPC directly against the emulator so it is a live resource this
|
|
116
|
+
estate does not own at an identity the `live-adopt/` root declares, run the
|
|
117
|
+
Ledger step and expect exactly one adoptable match carrying the two marker
|
|
118
|
+
values, run the Adopt step and let it write them, then re-plan and expect the
|
|
119
|
+
estate to own the same VPC with nothing left adoptable.
|
|
120
|
+
|
|
121
|
+
Gated on a `choudoufu` on PATH, on an `aws` CLI on PATH (the unmarked
|
|
122
|
+
resource is created and adopted through it), on
|
|
123
|
+
`CHOUDOUFU_EMULATOR_ENDPOINT`, and then on
|
|
124
|
+
`CHOUDOUFU_ADOPTABLE_NOT_IN_DOCUMENT`, which replaced the #894 gate and names
|
|
125
|
+
choudoufu #962.
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
npx vitest run lexicons/terraform/src/composites/terraform-adopt-op.acceptance.test.ts
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Last passed: never, and the reason changed on 2026-09-07 rather than going
|
|
132
|
+
away. chant #2168 dropped the #894 gate and ran the block against choudoufu
|
|
133
|
+
v0.14.0 and the pinned floci emulator. It reached the document and stopped
|
|
134
|
+
there, with `ledger.adoptions` empty:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
AssertionError: expected [] to have a length of 1 but got +0
|
|
138
|
+
❯ terraform-adopt-op.acceptance.test.ts:116:34
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The document's `unowned[]` is the resources found at an identity the
|
|
142
|
+
configuration itself declares, and it works: an unmarked
|
|
143
|
+
`aws_cloudwatch_log_group` in the same fixture shape comes back on the same
|
|
144
|
+
binary with `adopt_tofu_estate` and `adopt_tofu_address` on it, which is the
|
|
145
|
+
shape `../live-plan.json` recorded. An `aws_vpc` has no such identity, so the
|
|
146
|
+
document reports `omissions[].reason = "NEEDS_DISCOVERY"` and leaves
|
|
147
|
+
`unowned[]` empty. The VPC is matched by choudoufu's content matcher during
|
|
148
|
+
the estate-wide unclaimed sweep instead, and that match is printed only in the
|
|
149
|
+
human render's "Adoptable" section, for which `views.LivePlanDocument` has no
|
|
150
|
+
field. `-adoption-only` is refused alongside `-json`, and
|
|
151
|
+
`TOFU_LIVE_COLLECT_UNCLAIMED=1` on the `-json` run leaves `"unowned": []`
|
|
152
|
+
while the text run beside it prints `Adoptable: 1 live resource matches a
|
|
153
|
+
declared resource`. Filed upstream as choudoufu #962
|
|
154
|
+
(https://github.com/INTENTIUS/choudoufu/issues/962), which carries the full
|
|
155
|
+
measurement; chant #2168 has the same.
|
|
156
|
+
|
|
157
|
+
The fixture was deliberately left as an `aws_vpc`. A log group would make the
|
|
158
|
+
block pass and would stop it proving the content-matcher path, which is the
|
|
159
|
+
only thing it exists to prove.
|
|
160
|
+
|
|
161
|
+
## Running everything that can run on a machine with no choudoufu
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
npx vitest run \
|
|
165
|
+
lexicons/terraform/src/composites/terraform-apply-op.acceptance.test.ts \
|
|
166
|
+
lexicons/terraform/src/composites/terraform-adopt-op.acceptance.test.ts \
|
|
167
|
+
lexicons/terraform/src/op/activities/choudoufu.acceptance.test.ts
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
With `terraform` on PATH and the registry reachable, that is 1 passed and 5
|
|
171
|
+
skipped, and each skip names its own reason in the block title. With
|
|
172
|
+
choudoufu v0.14.0, the `aws` CLI and the emulator all present, it is 5 passed
|
|
173
|
+
and 1 skipped: block 4 is the skip. On 2026-09-07 that is exactly what it
|
|
174
|
+
printed.
|
|
175
|
+
|
|
176
|
+
## Keeping this file honest
|
|
177
|
+
|
|
178
|
+
Add a row when a suite is added, and update a "last passed" line in the same
|
|
179
|
+
change that makes a run happen. A line here that names no binary version and
|
|
180
|
+
no date is worth less than no line at all, so if a run cannot be reproduced,
|
|
181
|
+
say whose prose the claim comes from and that it was not reproduced.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_version = ">= 1.5.0"
|
|
3
|
+
|
|
4
|
+
live {
|
|
5
|
+
estate = "fixture-estate"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
required_providers {
|
|
9
|
+
null = {
|
|
10
|
+
source = "hashicorp/null"
|
|
11
|
+
version = "~> 3.2"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
provider "null" {}
|
|
17
|
+
|
|
18
|
+
resource "null_resource" "first" {
|
|
19
|
+
triggers = {
|
|
20
|
+
name = "first"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
resource "null_resource" "second" {
|
|
25
|
+
triggers = {
|
|
26
|
+
name = "second"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# The body of this file is the live block's content, with no wrapper block
|
|
2
|
+
# (choudoufu's live/MARKERS.md and site/content/docs/use/setup.md). The sidecar
|
|
3
|
+
# form rather than the in-block one, because choudoufu's own migration guide
|
|
4
|
+
# calls it the leading form: stock tooling never reads this file, so adopting
|
|
5
|
+
# live markers adds one file and changes zero existing lines.
|
|
6
|
+
estate = "chant-adopt-fixture"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_version = ">= 1.5.0"
|
|
3
|
+
|
|
4
|
+
required_providers {
|
|
5
|
+
aws = {
|
|
6
|
+
source = "hashicorp/aws"
|
|
7
|
+
version = "~> 6.0"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
provider "aws" {}
|
|
13
|
+
|
|
14
|
+
# A VPC the acceptance test creates out of band, with no ownership marker on
|
|
15
|
+
# it, before this root ever plans. choudoufu's content matcher offers a VPC on
|
|
16
|
+
# `cidr_block` (`matchTable` in choudoufu's internal/live/foreign/classify.go),
|
|
17
|
+
# so an unmarked live VPC at this exact cidr is what makes this root's plan
|
|
18
|
+
# report one adoptable match rather than proposing a second VPC beside it.
|
|
19
|
+
resource "aws_vpc" "adoptable" {
|
|
20
|
+
cidr_block = "10.77.0.0/16"
|
|
21
|
+
}
|