@intentius/chant-lexicon-terraform 0.57.0 → 0.58.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/dist/composites/terraform-adopt-op.d.ts +126 -0
- package/dist/composites/terraform-adopt-op.d.ts.map +1 -0
- package/dist/composites/terraform-apply-op.d.ts +63 -16
- package/dist/composites/terraform-apply-op.d.ts.map +1 -1
- package/dist/composites/terraform-watch-op.d.ts +80 -36
- 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 +142 -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 +32 -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 +68 -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 +18 -0
- 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 +21 -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 +21 -0
- package/dist/lint/post-synth/tf026.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 +24 -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/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 +445 -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 +46 -6
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/resolve-root-mode.d.ts +45 -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 +32 -8
- 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 +63 -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 +80 -0
- package/dist/skills/chant-terraform.md +1 -1
- package/package.json +7 -2
- 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/codegen/lint-rules-page.test.ts +28 -0
- package/src/composites/terraform-adopt-op.acceptance.test.ts +166 -0
- package/src/composites/terraform-adopt-op.test.ts +170 -0
- package/src/composites/terraform-adopt-op.ts +197 -0
- package/src/composites/terraform-apply-op.acceptance.test.ts +183 -5
- package/src/composites/terraform-apply-op.test.ts +167 -15
- package/src/composites/terraform-apply-op.ts +105 -28
- package/src/composites/terraform-watch-op.test.ts +85 -26
- package/src/composites/terraform-watch-op.ts +130 -73
- 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 +249 -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 +264 -3
- package/src/lint/audit-lineage.ts +319 -1
- package/src/lint/audit.test.ts +40 -1
- package/src/lint/post-synth/blocks.ts +109 -0
- package/src/lint/post-synth/fixtures/TF001/negative.tf +28 -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/load.ts +108 -0
- package/src/lint/post-synth/index.ts +48 -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 +14 -15
- 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 +32 -8
- 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 +63 -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 +80 -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 +10 -5
- package/src/op/activities/choudoufu.acceptance.test.ts +143 -0
- package/src/op/activities/choudoufu.test.ts +693 -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 +823 -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 +93 -5
- package/src/op/resolve-root-mode.test.ts +85 -0
- package/src/op/resolve-root-mode.ts +72 -0
- package/src/plugin.test.ts +68 -0
- package/src/plugin.ts +75 -9
- package/src/skills/chant-terraform.md +1 -1
|
@@ -45,7 +45,7 @@ describe("TerraformWatchOp phases (#2087)", () => {
|
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
test("the Plan step reports drift as a
|
|
48
|
+
test("the Plan step reports drift as a run outcome", () => {
|
|
49
49
|
const plan = props({ name: "app-watch", root: "app" }).phases[1].steps[0] as ActivityStep;
|
|
50
50
|
expect(plan.fn).toBe("terraformPlan");
|
|
51
51
|
expect(plan.id).toBe("plan");
|
|
@@ -145,40 +145,99 @@ describe("TerraformWatchOp posts the human plan and never the plan JSON (#2087)"
|
|
|
145
145
|
});
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
-
describe("TerraformWatchOp
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
describe("TerraformWatchOp on a live root (#2105)", () => {
|
|
149
|
+
const live = (over: Partial<Parameters<typeof TerraformWatchOp>[0]> = {}): OpConfig =>
|
|
150
|
+
props({ name: "estate-watch", root: "estate", live: true, ...over });
|
|
151
|
+
|
|
152
|
+
test("the Plan step is choudoufuLivePlan, and Init is unchanged", () => {
|
|
153
|
+
const op = live();
|
|
154
|
+
expect(phaseNames(op)).toEqual(["Init", "Plan"]);
|
|
155
|
+
expect(steps(op).map((s) => s.fn)).toEqual(["terraformInit", "choudoufuLivePlan"]);
|
|
153
156
|
});
|
|
154
157
|
|
|
155
|
-
test("
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
test("one live read publishes Drift, Unowned and Adoptable", () => {
|
|
159
|
+
const plan = live().phases[1].steps[0] as ActivityStep;
|
|
160
|
+
expect(plan.id).toBe("plan");
|
|
161
|
+
// `drift` off `-detailed-exitcode`, `unowned`/`adoptable` off the same
|
|
162
|
+
// run's #788 document: three answers, one estate-wide sweep.
|
|
163
|
+
expect(plan.outcomeAttribute).toEqual([
|
|
164
|
+
{ name: "Drift", from: "drift" },
|
|
165
|
+
{ name: "Unowned", from: "unowned" },
|
|
166
|
+
{ name: "Adoptable", from: "adoptable" },
|
|
167
|
+
]);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("the Op is marked live, and no plan file is named anywhere", () => {
|
|
171
|
+
const op = live();
|
|
172
|
+
expect(op.labels).toMatchObject({ TerraformMode: "live" });
|
|
173
|
+
expect(JSON.stringify(op)).not.toContain("chant.tfplan");
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("planFile on a live root is refused at build time, quoting choudoufu", () => {
|
|
177
|
+
expect(() => live({ planFile: "estate.tfplan" })).toThrow(/planFile is refused on a live root/);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("estate off a live root is refused: there is no live plan to name one for", () => {
|
|
181
|
+
expect(() => props({ name: "app-watch", root: "app", estate: "prod" })).toThrow(/not on a live root/);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("an explicit estate rides the Plan step", () => {
|
|
185
|
+
const plan = live({ estate: "prod-networking" }).phases[1].steps[0] as ActivityStep;
|
|
186
|
+
expect(plan.args?.estate).toBe("prod-networking");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("a stock root is untouched: still terraformPlan, still one Drift attribute", () => {
|
|
190
|
+
const plan = props({ name: "app-watch", root: "app" }).phases[1].steps[0] as ActivityStep;
|
|
191
|
+
expect(plan.fn).toBe("terraformPlan");
|
|
192
|
+
expect(plan.outcomeAttribute).toEqual({ name: "Drift", from: "changed" });
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
for (const mode of ["issue", "pull-request"] as const) {
|
|
196
|
+
test(`${mode}: the body is the plan text plus the adoption ledger, as one field`, () => {
|
|
197
|
+
const op = live({ findingMode: mode });
|
|
198
|
+
expect(phaseNames(op)).toEqual(["Init", "Plan", "Report"]);
|
|
199
|
+
const report = op.phases[2].steps[0] as ActivityStep;
|
|
200
|
+
expect(report.fn).toBe("reconcilePr");
|
|
201
|
+
expect(isStepOutputRef(report.args?.body)).toBe(true);
|
|
202
|
+
expect(report.args?.body).toMatchObject({ step: "plan", path: "finding" });
|
|
160
203
|
});
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
204
|
+
|
|
205
|
+
test(`${mode}: no reference to the live-plan document reaches the body`, () => {
|
|
206
|
+
// On a live root the JSON is choudoufu's bound/omissions/unowned
|
|
207
|
+
// document: live identities and tag values for every resource the
|
|
208
|
+
// estate touched. Same rule as the stock plan's JSON, same test.
|
|
209
|
+
const op = live({ findingMode: mode });
|
|
210
|
+
const report = op.phases[2].steps[0] as ActivityStep;
|
|
211
|
+
const paths = collectStepOutputRefs(report.args).map((r) => r.path);
|
|
212
|
+
expect(paths).toEqual(["finding"]);
|
|
213
|
+
expect(paths).not.toContain("json");
|
|
214
|
+
expect(JSON.stringify(report.args)).not.toContain("json");
|
|
168
215
|
});
|
|
169
|
-
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
test("the whole live Op references exactly one field of the plan", () => {
|
|
219
|
+
const op = live({ findingMode: "issue" });
|
|
220
|
+
const refs = op.phases.flatMap((p) => p.steps.filter(isActivity)).flatMap((s) => collectStepOutputRefs(s.args));
|
|
221
|
+
expect(refs).toHaveLength(1);
|
|
222
|
+
expect(refs[0]).toMatchObject({ step: "plan", path: "finding" });
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe("TerraformWatchOp schedule (#2087, #2120)", () => {
|
|
227
|
+
test("omitting `schedule` leaves the Op with no cadence", () => {
|
|
228
|
+
const { op } = TerraformWatchOp({ name: "app-watch", root: "app" });
|
|
229
|
+
expect((op as unknown as { props: OpConfig }).props).not.toHaveProperty("schedule");
|
|
170
230
|
});
|
|
171
231
|
|
|
172
|
-
test("
|
|
173
|
-
const { op
|
|
232
|
+
test("a cron lands on the Op itself", () => {
|
|
233
|
+
const { op } = TerraformWatchOp({
|
|
174
234
|
name: "app-watch",
|
|
175
235
|
root: "app",
|
|
176
236
|
schedule: "0 6 * * *",
|
|
177
|
-
taskQueue: "infra",
|
|
178
237
|
});
|
|
179
|
-
expect((op as unknown as { props: OpConfig }).props.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
)
|
|
238
|
+
expect((op as unknown as { props: OpConfig }).props.schedule).toEqual({
|
|
239
|
+
cron: "0 6 * * *",
|
|
240
|
+
overlap: "skip",
|
|
241
|
+
});
|
|
183
242
|
});
|
|
184
243
|
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* the plan found. `terraform plan -detailed-exitcode` is the whole mechanism.
|
|
8
8
|
* Exit 0 is "the world matches the configuration", exit 2 is drift, and
|
|
9
9
|
* `terraformPlan` turns that into the boolean `changed`, which rides out of
|
|
10
|
-
* the Op as the `Drift`
|
|
10
|
+
* the Op as the run's `Drift` outcome.
|
|
11
11
|
*
|
|
12
12
|
* Phases: Init, Plan, and (for a finding mode that opens something) Report.
|
|
13
13
|
*
|
|
@@ -23,37 +23,55 @@
|
|
|
23
23
|
* post the JSON, and `terraform-watch-op.test.ts` asserts no reference to it
|
|
24
24
|
* reaches the finding step.
|
|
25
25
|
*
|
|
26
|
+
* The same rule holds on a live root, where the JSON is choudoufu's own
|
|
27
|
+
* bound/omissions/unowned document rather than a plan representation, and
|
|
28
|
+
* carries live identities and tag values for every resource the estate
|
|
29
|
+
* touched. `choudoufuLivePlan` composes the plan text and the adoption ledger
|
|
30
|
+
* into one `finding` string for exactly this reason: the body is one
|
|
31
|
+
* reference to one text field, so there is no arrangement of the args in
|
|
32
|
+
* which the document reaches a body.
|
|
33
|
+
*
|
|
34
|
+
* ## A live root reports three things, not one (#2105)
|
|
35
|
+
*
|
|
36
|
+
* `live: true` swaps the Plan step for `choudoufuLivePlan`. On a choudoufu
|
|
37
|
+
* root ownership is a pair of tags on the resource rather than an entry in a
|
|
38
|
+
* state file, so a plan against the live system can answer three questions
|
|
39
|
+
* where a stock plan answers one: whether the estate drifted (the
|
|
40
|
+
* `-detailed-exitcode` exit, `Drift` as before), how many live resources sit
|
|
41
|
+
* at a declared identity carrying no marker (`Unowned`), and how many of those
|
|
42
|
+
* an exact content match makes claimable (`Adoptable`). All three come off a
|
|
43
|
+
* single live read, published as three run outcomes from the one step.
|
|
44
|
+
*
|
|
45
|
+
* The finding modes then carry the adoption ledger under the plan text: one
|
|
46
|
+
* line per adoptable match with its address, its live identity and the
|
|
47
|
+
* `tofu-estate`/`tofu-address` values that claim it, in the row form
|
|
48
|
+
* `live-plan -adoption-only` prints, followed by any contested address. That
|
|
49
|
+
* is a report, not an action. `TerraformAdoptOp` next door is what writes the
|
|
50
|
+
* markers, and it gates first.
|
|
51
|
+
*
|
|
26
52
|
* ## Findings reuse `reconcilePr`
|
|
27
53
|
*
|
|
28
|
-
* `issue` and `pull-request` call
|
|
29
|
-
*
|
|
30
|
-
*
|
|
54
|
+
* `issue` and `pull-request` call core's `reconcilePr` activity, the one place
|
|
55
|
+
* in chant that shells to `gh issue create` / `gh pr create`, rather than
|
|
56
|
+
* growing a second copy of those calls here.
|
|
31
57
|
* That activity built its own change-set summary and had no way to be handed
|
|
32
58
|
* one, so #2087 added a single field to it (`ReconcilePrArgs.body`), which is
|
|
33
59
|
* what carries the `-no-color` plan through. Note that the pull-request mode
|
|
34
60
|
* of `reconcilePr` regenerates chant TypeScript via `chant import`; a
|
|
35
|
-
* terraform-native regeneration of HCL from live state is #2089, so
|
|
36
|
-
*
|
|
37
|
-
* `"pull-request"` opens a PR whose body is the plan.
|
|
61
|
+
* terraform-native regeneration of HCL from live state is #2089, so
|
|
62
|
+
* `findingMode: "issue"` is the mode with an end-to-end answer and
|
|
63
|
+
* `"pull-request"` opens a PR whose body is the plan. On a live root #2089's
|
|
64
|
+
* question has a different answer rather than a pending one, and it is not
|
|
65
|
+
* regeneration: see `TerraformAdoptOp`.
|
|
38
66
|
*
|
|
39
67
|
* ## The schedule
|
|
40
68
|
*
|
|
41
|
-
* A cron
|
|
42
|
-
* `
|
|
43
|
-
* `
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* project that installs terraform and runs `chant run` on the local executor
|
|
48
|
-
* could not load `@intentius/chant-lexicon-terraform` at all without also
|
|
49
|
-
* installing temporal. The declarable is the same one temporal's
|
|
50
|
-
* `TemporalSchedule` produces (`createResource("Temporal::Schedule",
|
|
51
|
-
* "temporal", {})`, `lexicons/temporal/src/resources.ts`), so temporal's
|
|
52
|
-
* serializer renders it unchanged when a project has both.
|
|
53
|
-
*
|
|
54
|
-
* A project with no Temporal at all runs the same Op on a CI cron instead:
|
|
55
|
-
* `generateOpsPipeline` against the github lexicon turns the Op plus its cron
|
|
56
|
-
* into a workflow. `examples/scheduled-watch/` is that recipe.
|
|
69
|
+
* A cron lands on the Op itself as `schedule` (#2120), the same field
|
|
70
|
+
* `WatchOp` and `ConvergeOp` set. It is runtime-neutral data: `chant
|
|
71
|
+
* operator` reads it as this Op's tick cadence, `generateOpsPipeline` against
|
|
72
|
+
* the github lexicon renders it as a CI cron, and a hosting lexicon hands it
|
|
73
|
+
* to its own scheduler. The one-shot `chant run app-watch` path ignores it.
|
|
74
|
+
* `examples/scheduled-watch/` is the CI recipe.
|
|
57
75
|
*
|
|
58
76
|
* @example
|
|
59
77
|
* ```typescript
|
|
@@ -63,20 +81,30 @@
|
|
|
63
81
|
* export const { op } = TerraformWatchOp({ name: "app-watch", root: "app" });
|
|
64
82
|
*
|
|
65
83
|
* // nightly, opening an issue when the plan is non-empty
|
|
66
|
-
* export const { op
|
|
84
|
+
* export const { op } = TerraformWatchOp({
|
|
67
85
|
* name: "app-watch",
|
|
68
86
|
* root: "app",
|
|
69
87
|
* schedule: "0 6 * * *",
|
|
70
88
|
* findingMode: "issue",
|
|
71
89
|
* });
|
|
90
|
+
*
|
|
91
|
+
* // a choudoufu estate: drift, unowned and adoptable, with the ledger in the issue
|
|
92
|
+
* export const { op } = TerraformWatchOp({
|
|
93
|
+
* name: "estate-watch",
|
|
94
|
+
* root: "estate",
|
|
95
|
+
* live: true,
|
|
96
|
+
* findingMode: "issue",
|
|
97
|
+
* });
|
|
72
98
|
* ```
|
|
73
99
|
*/
|
|
74
100
|
|
|
75
101
|
import { Op, phase, OpResource, type ActivityStep } from "@intentius/chant/op";
|
|
76
|
-
import {
|
|
77
|
-
import
|
|
78
|
-
|
|
79
|
-
|
|
102
|
+
import { CHOUDOUFU_LIVE_PLAN_OUT_REFUSAL, DEFAULT_PLAN_FILE } from "../op/activities/terraform";
|
|
103
|
+
import {
|
|
104
|
+
terraformInit as initStep,
|
|
105
|
+
terraformPlan as planStep,
|
|
106
|
+
choudoufuLivePlan as livePlanStep,
|
|
107
|
+
} from "../op/builders";
|
|
80
108
|
|
|
81
109
|
/**
|
|
82
110
|
* What to do with a non-empty plan. A subset of core's `OpFindingMode`
|
|
@@ -85,36 +113,47 @@ import { terraformInit as initStep, terraformPlan as planStep } from "../op/buil
|
|
|
85
113
|
*/
|
|
86
114
|
export type TerraformFindingMode = "report" | "issue" | "pull-request";
|
|
87
115
|
|
|
88
|
-
/**
|
|
89
|
-
* `Temporal::Schedule`, built without importing the temporal lexicon. See the
|
|
90
|
-
* module doc for why. Identical to `TemporalSchedule` in
|
|
91
|
-
* `lexicons/temporal/src/resources.ts`.
|
|
92
|
-
*/
|
|
93
|
-
const ScheduleResource = createResource("Temporal::Schedule", "temporal", {});
|
|
94
|
-
|
|
95
|
-
/** `app-watch` becomes `appWatchWorkflow`, matching the temporal serializer's naming. */
|
|
96
|
-
function kebabToCamel(s: string): string {
|
|
97
|
-
return s.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());
|
|
98
|
-
}
|
|
99
|
-
|
|
100
116
|
export interface TerraformWatchOpConfig {
|
|
101
|
-
/** Op name (kebab-case).
|
|
117
|
+
/** Op name (kebab-case). */
|
|
102
118
|
name: string;
|
|
103
119
|
/** Key into the project's `terraform.roots`. The root carries dir, workspace, var files and backend config. */
|
|
104
120
|
root: string;
|
|
105
121
|
/**
|
|
106
|
-
* Cron expression. When set,
|
|
107
|
-
*
|
|
108
|
-
*
|
|
122
|
+
* Cron expression. When set, it lands on the Op as `schedule` — the cadence
|
|
123
|
+
* `chant operator` ticks on and `generateOpsPipeline` renders as a CI cron.
|
|
124
|
+
* Omit for one-shot `chant run` on the local executor.
|
|
109
125
|
*/
|
|
110
126
|
schedule?: string;
|
|
111
127
|
/**
|
|
112
128
|
* What to do when the plan proposes changes. Default: `"report"`, which
|
|
113
|
-
* opens nothing. The `Drift`
|
|
129
|
+
* opens nothing. The `Drift` run outcome and the run's own log are
|
|
114
130
|
* the report.
|
|
115
131
|
*/
|
|
116
132
|
findingMode?: TerraformFindingMode;
|
|
117
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* Watch a live root (#2105): the root runs `terraform.binary: "choudoufu"`
|
|
135
|
+
* and declares an estate, so the Plan phase is `choudoufuLivePlan` rather
|
|
136
|
+
* than `terraformPlan`. See the module doc for what changes.
|
|
137
|
+
*
|
|
138
|
+
* Declared here rather than detected, because a composite is built by
|
|
139
|
+
* `chant build` without reading a root module's `.tf` files. The activities
|
|
140
|
+
* detect the estate at run time (`op/activities/live-detect.ts`), but by then
|
|
141
|
+
* the phases are already serialized. Setting this on a stock root fails at
|
|
142
|
+
* the first step, with choudoufu's own reason.
|
|
143
|
+
*/
|
|
144
|
+
live?: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* The estate to plan against, for a live root whose estate this Op should
|
|
147
|
+
* name explicitly. Omitted, `choudoufuLivePlan` auto-detects it from the
|
|
148
|
+
* root's own `live` block or `estate.chdf.hcl` sidecar, which is the usual
|
|
149
|
+
* case. Refused on a stock root.
|
|
150
|
+
*/
|
|
151
|
+
estate?: string;
|
|
152
|
+
/**
|
|
153
|
+
* Plan file the Plan step writes, relative to the root dir. Default:
|
|
154
|
+
* `chant.tfplan`. Refused on a live root, where choudoufu takes no plan file
|
|
155
|
+
* at all.
|
|
156
|
+
*/
|
|
118
157
|
planFile?: string;
|
|
119
158
|
/** `-upgrade` on the Init step: re-resolve provider and module versions. */
|
|
120
159
|
upgrade?: boolean;
|
|
@@ -129,27 +168,49 @@ export interface TerraformWatchOpConfig {
|
|
|
129
168
|
title?: string;
|
|
130
169
|
/** Branch the pull-request mode opens from. Default: `reconcilePr`'s own. */
|
|
131
170
|
branch?: string;
|
|
132
|
-
/** Override the task queue. Defaults to `name`. */
|
|
133
|
-
taskQueue?: string;
|
|
134
171
|
}
|
|
135
172
|
|
|
136
173
|
export interface TerraformWatchOpResources {
|
|
137
|
-
/** Op resource. Generates the Init/Plan/[Report]
|
|
174
|
+
/** Op resource. Generates the Init/Plan/[Report] phases. */
|
|
138
175
|
op: InstanceType<typeof OpResource>;
|
|
139
|
-
/** `Temporal::Schedule`, present only when `schedule` was given. */
|
|
140
|
-
schedule?: Declarable;
|
|
141
176
|
}
|
|
142
177
|
|
|
143
178
|
export function TerraformWatchOp(config: TerraformWatchOpConfig): TerraformWatchOpResources {
|
|
144
|
-
const taskQueue = config.taskQueue ?? config.name;
|
|
145
|
-
const planFile = config.planFile ?? DEFAULT_PLAN_FILE;
|
|
146
179
|
const findingMode: TerraformFindingMode = config.findingMode ?? "report";
|
|
147
180
|
const where = config.cwd ? { cwd: config.cwd } : {};
|
|
148
181
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
182
|
+
if (config.live && config.planFile !== undefined) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`TerraformWatchOp "${config.name}": planFile is refused on a live root. choudoufu: ` +
|
|
185
|
+
`"${CHOUDOUFU_LIVE_PLAN_OUT_REFUSAL}". A live plan writes no saved plan, so there is no file to name; ` +
|
|
186
|
+
"drop planFile, or drop live if this root runs stock.",
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
if (!config.live && config.estate !== undefined) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
`TerraformWatchOp "${config.name}": estate names the estate a live plan looks for markers of, and ` +
|
|
192
|
+
"this Op is not on a live root. Set live: true, or drop estate.",
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// `id` is what makes `plan.out` legal, and one field of `plan.out` is the
|
|
197
|
+
// only channel by which any part of the plan leaves this Op.
|
|
198
|
+
//
|
|
199
|
+
// A live root reads the live system instead of a state file, so the three
|
|
200
|
+
// things a watch reports come off one `choudoufuLivePlan`: `drift` from
|
|
201
|
+
// `-detailed-exitcode`, and `unowned`/`adoptable` from the `-json` document
|
|
202
|
+
// the same run wrote. Three attributes, one live read: see
|
|
203
|
+
// `ActivityStep.outcomeAttribute`'s array form (#2105).
|
|
204
|
+
const plan = config.live
|
|
205
|
+
? livePlanStep(config.root, { ...where, ...(config.estate ? { estate: config.estate } : {}), id: "plan" })
|
|
206
|
+
: planStep(config.root, { planFile: config.planFile ?? DEFAULT_PLAN_FILE, ...where, id: "plan" });
|
|
207
|
+
plan.outcomeAttribute = config.live
|
|
208
|
+
? [
|
|
209
|
+
{ name: "Drift", from: "drift" },
|
|
210
|
+
{ name: "Unowned", from: "unowned" },
|
|
211
|
+
{ name: "Adoptable", from: "adoptable" },
|
|
212
|
+
]
|
|
213
|
+
: { name: "Drift", from: "changed" };
|
|
153
214
|
|
|
154
215
|
const phases = [
|
|
155
216
|
phase("Init", [initStep(config.root, { ...where, ...(config.upgrade ? { upgrade: true } : {}) })]),
|
|
@@ -169,7 +230,11 @@ export function TerraformWatchOp(config: TerraformWatchOpConfig): TerraformWatch
|
|
|
169
230
|
mode: findingMode,
|
|
170
231
|
entries: [],
|
|
171
232
|
title: config.title ?? `Terraform drift in root "${config.root}"`,
|
|
172
|
-
|
|
233
|
+
// Stock: the `-no-color` plan. Live: the same plan text with the
|
|
234
|
+
// adoption ledger under it, which `choudoufuLivePlan` composes into
|
|
235
|
+
// one `finding` field precisely so that a body is one reference and
|
|
236
|
+
// the `-json` document has no path into one at all.
|
|
237
|
+
body: config.live ? plan.out.finding : plan.out.text,
|
|
173
238
|
...(config.branch ? { branch: config.branch } : {}),
|
|
174
239
|
},
|
|
175
240
|
outcomeAttribute:
|
|
@@ -180,25 +245,17 @@ export function TerraformWatchOp(config: TerraformWatchOpConfig): TerraformWatch
|
|
|
180
245
|
|
|
181
246
|
const op = Op({
|
|
182
247
|
name: config.name,
|
|
183
|
-
overview:
|
|
184
|
-
|
|
185
|
-
|
|
248
|
+
overview: config.live
|
|
249
|
+
? `Live-plan the "${config.root}" choudoufu estate and report drift, unowned and adoptable resources`
|
|
250
|
+
: `Plan the "${config.root}" terraform root and report drift`,
|
|
251
|
+
labels: {
|
|
186
252
|
Watch: "true",
|
|
187
253
|
TerraformRoot: config.root,
|
|
254
|
+
...(config.live ? { TerraformMode: "live" } : {}),
|
|
188
255
|
},
|
|
256
|
+
...(config.schedule ? { schedule: { cron: config.schedule, overlap: "skip" as const } } : {}),
|
|
189
257
|
phases,
|
|
190
258
|
});
|
|
191
259
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const schedule = new ScheduleResource({
|
|
195
|
-
scheduleId: `${config.name}-schedule`,
|
|
196
|
-
spec: { cronExpressions: [config.schedule] },
|
|
197
|
-
action: {
|
|
198
|
-
workflowType: kebabToCamel(config.name) + "Workflow",
|
|
199
|
-
taskQueue,
|
|
200
|
-
},
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
return { op, schedule };
|
|
260
|
+
return { op };
|
|
204
261
|
}
|
package/src/config.ts
CHANGED
|
@@ -25,6 +25,20 @@
|
|
|
25
25
|
import { z } from "zod";
|
|
26
26
|
import type { ChantConfig } from "@intentius/chant/config";
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* chant's cross-lexicon delete-mode vocabulary (`lexicons/k8s/src/op/
|
|
30
|
+
* activities/kubectl.ts`'s `ApplyDeleteMode`, `packages/core/src/op/
|
|
31
|
+
* activities/apply.ts`'s `DeleteMode`), read here for a live root and mapped
|
|
32
|
+
* onto choudoufu's `policy` block (#2106): `"never"` requires the root's
|
|
33
|
+
* `policy` to set `undeclared_tagged` to `"keep"`, `"untag"` or `"report"`
|
|
34
|
+
* (TF026 enforces this at build time); `"owned-only"` is choudoufu's own
|
|
35
|
+
* default verb for that quadrant (`delete`) and needs nothing; `"gated"`
|
|
36
|
+
* needs nothing beyond `TerraformApplyOp`'s own approval gate. Inert on a
|
|
37
|
+
* stock root: choudoufu's `policy` block does not exist there.
|
|
38
|
+
*/
|
|
39
|
+
export const terraformDeleteModeSchema = z.enum(["never", "owned-only", "gated"]);
|
|
40
|
+
export type TerraformDeleteMode = z.infer<typeof terraformDeleteModeSchema>;
|
|
41
|
+
|
|
28
42
|
/**
|
|
29
43
|
* One Terraform root module chant reads. `dir` is the only required field:
|
|
30
44
|
* everything else names how the root is invoked rather than what it declares.
|
|
@@ -43,14 +57,29 @@ export const terraformRootSchema = z.strictObject({
|
|
|
43
57
|
varFiles: z.array(z.string()).optional(),
|
|
44
58
|
/** `-backend-config` key/value pairs handed to `init`. */
|
|
45
59
|
backendConfig: z.record(z.string(), z.string()).optional(),
|
|
60
|
+
/** This root's declared delete mode. See {@link terraformDeleteModeSchema}. */
|
|
61
|
+
delete: terraformDeleteModeSchema.optional(),
|
|
46
62
|
});
|
|
47
63
|
|
|
48
64
|
export const terraformConfigSchema = z.strictObject({
|
|
49
65
|
/**
|
|
50
|
-
* Which CLI drives the roots.
|
|
51
|
-
* this lexicon does, so the choice is recorded rather than
|
|
66
|
+
* Which CLI drives the roots. `terraform` and `tofu` are wire-compatible
|
|
67
|
+
* for everything this lexicon does, so the choice is recorded rather than
|
|
68
|
+
* inferred. `choudoufu` (#2103) is an OpenTofu fork: a root running it also
|
|
69
|
+
* needs a declared estate (a `live` block or an `estate.chdf.hcl` sidecar,
|
|
70
|
+
* seen by the shared HCL parse) before it counts as live. Declaring the
|
|
71
|
+
* binary alone runs the root stock, exactly as `terraform`/`tofu` do.
|
|
72
|
+
*/
|
|
73
|
+
binary: z.enum(["terraform", "tofu", "choudoufu"]).optional(),
|
|
74
|
+
/**
|
|
75
|
+
* How far a root's parse follows its `module` calls (#2112), spelled the
|
|
76
|
+
* way tflint spells it. `local` (the default) reads modules sourced from a
|
|
77
|
+
* relative path, so TF014, TF015 and TF020 see inside them; `none` reads
|
|
78
|
+
* only each root's own directory; `all` is reserved for fetching registry
|
|
79
|
+
* and git modules and is refused with a message, since chant fetches
|
|
80
|
+
* nothing. See `hcl/descend.ts`.
|
|
52
81
|
*/
|
|
53
|
-
|
|
82
|
+
callModuleType: z.enum(["local", "none", "all"]).optional(),
|
|
54
83
|
/** Named root modules. The name is the entity-key prefix, so keep it stable. */
|
|
55
84
|
roots: z.record(z.string(), terraformRootSchema),
|
|
56
85
|
});
|