@intentius/chant 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/audit/catalog.d.ts +51 -1
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/core.d.ts +20 -1
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +14 -8
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/lineage.d.ts +10 -0
- package/dist/audit/lineage.d.ts.map +1 -0
- package/dist/audit/prior-art.d.ts +385 -0
- package/dist/audit/prior-art.d.ts.map +1 -0
- package/dist/audit/proof.d.ts +16 -0
- package/dist/audit/proof.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +8 -1
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/report.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts +3 -0
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/audit/secrets.d.ts +15 -0
- package/dist/audit/secrets.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +46 -0
- package/dist/audit/terraform-state.d.ts.map +1 -0
- package/dist/build.d.ts +17 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/claimed-fields.d.ts +161 -0
- package/dist/claimed-fields.d.ts.map +1 -0
- package/dist/cli/build-params-cli.d.ts +2 -2
- package/dist/cli/command-group.d.ts +14 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +17 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +2 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +82 -16
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +49 -42
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts +28 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts +16 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/resources/context.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +26 -9
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
- package/dist/components/auto-release.d.ts +10 -20
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/capability.d.ts +4 -4
- package/dist/components/cli-support.d.ts +20 -21
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +3 -3
- package/dist/components/component.d.ts.map +1 -1
- package/dist/components/driver-output.d.ts.map +1 -1
- package/dist/components/driver.d.ts +66 -36
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/manifest-persistence.d.ts +1 -9
- package/dist/components/manifest-persistence.d.ts.map +1 -1
- package/dist/components/run-progress.d.ts +6 -6
- package/dist/components/run-progress.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts +9 -15
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/components/verbs/verify.d.ts +2 -2
- package/dist/deep-observation.d.ts +17 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/discovery/files.d.ts +4 -4
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +33 -0
- package/dist/discovery/param-deps.d.ts.map +1 -1
- package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
- package/dist/discovery/sandbox/config-wire.d.ts +2 -2
- package/dist/fold/foldable-helpers.d.ts +5 -1
- package/dist/fold/foldable-helpers.d.ts.map +1 -1
- package/dist/fold-provenance.d.ts +236 -0
- package/dist/fold-provenance.d.ts.map +1 -0
- package/dist/graph-ops.d.ts +1 -1
- package/dist/held-elsewhere.d.ts +85 -0
- package/dist/held-elsewhere.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/kubectl-context.d.ts +3 -3
- package/dist/lexicon-config.d.ts +4 -4
- package/dist/lexicon-plugin-helpers.d.ts +1 -1
- package/dist/lexicon.d.ts +104 -14
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/change-set.d.ts +13 -0
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/deep-diff.d.ts +133 -6
- package/dist/lifecycle/deep-diff.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +71 -3
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +2 -2
- package/dist/lifecycle/run-ledger.d.ts +53 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -0
- package/dist/lifecycle/symptoms.d.ts +3 -3
- package/dist/lifecycle/symptoms.d.ts.map +1 -1
- package/dist/lint/config.d.ts +81 -2
- package/dist/lint/config.d.ts.map +1 -1
- package/dist/lint/post-synth.d.ts +55 -2
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
- package/dist/lint/rules/comp/index.d.ts +1 -1
- package/dist/lint/rules/comp/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.d.ts +16 -0
- package/dist/lint/rules/op/index.d.ts.map +1 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
- package/dist/lint/rules/op/support.d.ts +12 -0
- package/dist/lint/rules/op/support.d.ts.map +1 -0
- package/dist/lint/suppressions.d.ts +108 -0
- package/dist/lint/suppressions.d.ts.map +1 -0
- package/dist/live-endpoint.d.ts +1 -1
- package/dist/op/activities/activity-contracts.d.ts +54 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -0
- package/dist/op/activities/apply.d.ts +262 -0
- package/dist/op/activities/apply.d.ts.map +1 -0
- package/dist/op/activities/build.d.ts +13 -0
- package/dist/op/activities/build.d.ts.map +1 -0
- package/dist/op/activities/converge.d.ts +110 -0
- package/dist/op/activities/converge.d.ts.map +1 -0
- package/dist/op/activities/env-teardown.d.ts +76 -0
- package/dist/op/activities/env-teardown.d.ts.map +1 -0
- package/dist/op/activities/guard-validate.d.ts +64 -0
- package/dist/op/activities/guard-validate.d.ts.map +1 -0
- package/dist/op/activities/http-check.d.ts +34 -0
- package/dist/op/activities/http-check.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +31 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
- package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
- package/dist/op/activities/lifecycle.d.ts +42 -0
- package/dist/op/activities/lifecycle.d.ts.map +1 -0
- package/dist/op/activities/pipeline-audit.d.ts +84 -0
- package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
- package/dist/op/activities/policy.d.ts +17 -0
- package/dist/op/activities/policy.d.ts.map +1 -0
- package/dist/op/activities/reconcile.d.ts +89 -0
- package/dist/op/activities/reconcile.d.ts.map +1 -0
- package/dist/op/activities/shell.d.ts +13 -0
- package/dist/op/activities/shell.d.ts.map +1 -0
- package/dist/op/activities/teardown.d.ts +10 -0
- package/dist/op/activities/teardown.d.ts.map +1 -0
- package/dist/op/activities/util.d.ts +7 -0
- package/dist/op/activities/util.d.ts.map +1 -0
- package/dist/op/activities/wait.d.ts +28 -0
- package/dist/op/activities/wait.d.ts.map +1 -0
- package/dist/op/activities/workflow-audit.d.ts +92 -0
- package/dist/op/activities/workflow-audit.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts +76 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -0
- package/dist/op/activity-contract.d.ts +25 -18
- package/dist/op/activity-contract.d.ts.map +1 -1
- package/dist/op/activity-failure.d.ts +28 -0
- package/dist/op/activity-failure.d.ts.map +1 -0
- package/dist/op/activity-profiles.d.ts +118 -0
- package/dist/op/activity-profiles.d.ts.map +1 -0
- package/dist/op/activity-registry.d.ts +22 -38
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-runtime.d.ts +3 -14
- package/dist/op/activity-runtime.d.ts.map +1 -1
- package/dist/op/builders.d.ts +84 -29
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/composites/apply-op.d.ts +113 -0
- package/dist/op/composites/apply-op.d.ts.map +1 -0
- package/dist/op/composites/converge-op.d.ts +120 -0
- package/dist/op/composites/converge-op.d.ts.map +1 -0
- package/dist/op/composites/index.d.ts +26 -0
- package/dist/op/composites/index.d.ts.map +1 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
- package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
- package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
- package/dist/op/composites/reconcile-op.d.ts +57 -0
- package/dist/op/composites/reconcile-op.d.ts.map +1 -0
- package/dist/op/composites/watch-op.d.ts +60 -0
- package/dist/op/composites/watch-op.d.ts.map +1 -0
- package/dist/op/composites/workflow-audit-op.d.ts +57 -0
- package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
- package/dist/op/converge-rule.d.ts +12 -10
- package/dist/op/converge-rule.d.ts.map +1 -1
- package/dist/op/cron.d.ts +51 -0
- package/dist/op/cron.d.ts.map +1 -0
- package/dist/op/discover.d.ts +9 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/duration.d.ts +11 -0
- package/dist/op/duration.d.ts.map +1 -0
- package/dist/op/emulator-lifecycle.d.ts.map +1 -1
- package/dist/op/gate.d.ts +90 -0
- package/dist/op/gate.d.ts.map +1 -0
- package/dist/op/generate-pipeline.d.ts +9 -0
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/index.d.ts +23 -7
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +114 -28
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-output.d.ts +11 -1
- package/dist/op/local-output.d.ts.map +1 -1
- package/dist/op/op-ir.d.ts +184 -0
- package/dist/op/op-ir.d.ts.map +1 -0
- package/dist/op/op-verb-class.d.ts +10 -2
- package/dist/op/op-verb-class.d.ts.map +1 -1
- package/dist/op/operator.d.ts +30 -5
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/receipt-store.d.ts +1 -1
- package/dist/op/receipt-store.d.ts.map +1 -1
- package/dist/op/resource.d.ts +15 -1
- package/dist/op/resource.d.ts.map +1 -1
- package/dist/op/runtime.d.ts +200 -0
- package/dist/op/runtime.d.ts.map +1 -0
- package/dist/op/runtimes/local.d.ts +29 -0
- package/dist/op/runtimes/local.d.ts.map +1 -0
- package/dist/op/step-output-ref.d.ts +15 -13
- package/dist/op/step-output-ref.d.ts.map +1 -1
- package/dist/op/types.d.ts +79 -27
- package/dist/op/types.d.ts.map +1 -1
- package/dist/ownership.d.ts +1 -1
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -1
- package/dist/serializer-walker.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/audit/catalog.test.ts +79 -3
- package/src/audit/catalog.ts +111 -13
- package/src/audit/core.test.ts +26 -0
- package/src/audit/core.ts +61 -17
- package/src/audit/discover.test.ts +39 -0
- package/src/audit/discover.ts +91 -15
- package/src/audit/lineage.ts +100 -0
- package/src/audit/prior-art.ts +153 -0
- package/src/audit/proof.test.ts +67 -0
- package/src/audit/proof.ts +70 -0
- package/src/audit/report-html.ts +9 -4
- package/src/audit/report-model.ts +16 -2
- package/src/audit/report.test.ts +33 -0
- package/src/audit/report.ts +5 -3
- package/src/audit/rules-doc.test.ts +34 -2
- package/src/audit/rules-doc.ts +82 -9
- package/src/audit/secrets.ts +15 -3
- package/src/audit/terraform-state.test.ts +144 -0
- package/src/audit/terraform-state.ts +131 -0
- package/src/build.ts +31 -1
- package/src/claimed-fields.test.ts +145 -0
- package/src/claimed-fields.ts +208 -0
- package/src/cli/build-params-cli.ts +2 -2
- package/src/cli/command-group.ts +14 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
- package/src/cli/commands/audit.test.ts +189 -11
- package/src/cli/commands/audit.ts +73 -27
- package/src/cli/commands/build.test.ts +292 -9
- package/src/cli/commands/build.ts +156 -24
- package/src/cli/commands/check-lexicon-docs.ts +1 -1
- package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
- package/src/cli/commands/check-lexicon-examples.ts +42 -10
- package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
- package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
- package/src/cli/commands/init-lexicon.test.ts +97 -2
- package/src/cli/commands/init-lexicon.ts +4 -2
- package/src/cli/commands/init.test.ts +35 -1
- package/src/cli/commands/init.ts +14 -3
- package/src/cli/commands/lint.test.ts +48 -0
- package/src/cli/commands/lint.ts +123 -0
- package/src/cli/commands/onboard.test.ts +75 -0
- package/src/cli/commands/onboard.ts +54 -23
- package/src/cli/handlers/build.test.ts +81 -1
- package/src/cli/handlers/build.ts +15 -1
- package/src/cli/handlers/graph.test.ts +3 -3
- package/src/cli/handlers/lifecycle.test.ts +195 -12
- package/src/cli/handlers/lifecycle.ts +123 -6
- package/src/cli/handlers/misc.ts +1 -1
- package/src/cli/handlers/operator.test.ts +183 -11
- package/src/cli/handlers/operator.ts +260 -55
- package/src/cli/handlers/run.test.ts +431 -1287
- package/src/cli/handlers/run.ts +365 -915
- package/src/cli/main.test.ts +23 -15
- package/src/cli/main.ts +88 -33
- package/src/cli/mcp/op-tools.ts +147 -88
- package/src/cli/mcp/resource-handlers.ts +10 -37
- package/src/cli/mcp/resources/context.ts +11 -12
- package/src/cli/mcp/server.test.ts +25 -27
- package/src/cli/mcp/server.ts +2 -2
- package/src/cli/registry.ts +26 -9
- package/src/codegen/docs.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.ts +1 -1
- package/src/components/README.md +3 -2
- package/src/components/auto-release.test.ts +4 -20
- package/src/components/auto-release.ts +17 -42
- package/src/components/capability.ts +4 -4
- package/src/components/cli-support.test.ts +10 -5
- package/src/components/cli-support.ts +52 -44
- package/src/components/component.schema.json +2 -2
- package/src/components/component.ts +3 -3
- package/src/components/driver-output.ts +14 -2
- package/src/components/driver.test.ts +92 -22
- package/src/components/driver.ts +193 -83
- package/src/components/index.ts +1 -6
- package/src/components/manifest-persistence.test.ts +2 -20
- package/src/components/manifest-persistence.ts +4 -24
- package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
- package/src/components/run-progress.ts +6 -6
- package/src/components/verbs/run-agent.ts +11 -17
- package/src/components/verbs/verify.ts +2 -2
- package/src/deep-observation.test.ts +75 -0
- package/src/deep-observation.ts +48 -0
- package/src/discovery/collect.test.ts +3 -3
- package/src/discovery/files.ts +4 -4
- package/src/discovery/fold-import.ts +105 -2
- package/src/discovery/param-deps.test.ts +112 -1
- package/src/discovery/param-deps.ts +193 -0
- package/src/discovery/sandbox/bundle.ts +15 -1
- package/src/discovery/sandbox/config-wire.test.ts +2 -2
- package/src/discovery/sandbox/config-wire.ts +2 -2
- package/src/fold/fold.test.ts +56 -0
- package/src/fold/foldable-helpers.ts +81 -1
- package/src/fold-provenance-exclusion.test.ts +186 -0
- package/src/fold-provenance.test.ts +495 -0
- package/src/fold-provenance.ts +441 -0
- package/src/graph-ops.test.ts +2 -2
- package/src/graph-ops.ts +1 -1
- package/src/held-elsewhere.test.ts +102 -0
- package/src/held-elsewhere.ts +114 -0
- package/src/index.ts +3 -0
- package/src/kubectl-context.ts +3 -3
- package/src/lexicon-config.test.ts +5 -5
- package/src/lexicon-config.ts +4 -4
- package/src/lexicon-plugin-helpers.ts +1 -1
- package/src/lexicon.ts +120 -14
- package/src/lifecycle/change-set.test.ts +49 -0
- package/src/lifecycle/change-set.ts +67 -0
- package/src/lifecycle/converge-ledger.ts +1 -1
- package/src/lifecycle/deep-diff.test.ts +267 -14
- package/src/lifecycle/deep-diff.ts +217 -14
- package/src/lifecycle/deep-observe.test.ts +3 -1
- package/src/lifecycle/deep-observe.ts +16 -6
- package/src/lifecycle/gate-ledger.test.ts +74 -2
- package/src/lifecycle/gate-ledger.ts +143 -36
- package/src/lifecycle/index.ts +1 -0
- package/src/lifecycle/release-ledger.ts +2 -2
- package/src/lifecycle/run-ledger.test.ts +268 -0
- package/src/lifecycle/run-ledger.ts +201 -0
- package/src/lifecycle/symptoms.ts +3 -3
- package/src/lint/config.test.ts +82 -1
- package/src/lint/config.ts +112 -3
- package/src/lint/policy.test.ts +1 -1
- package/src/lint/post-synth.test.ts +31 -0
- package/src/lint/post-synth.ts +54 -1
- package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
- package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
- package/src/lint/rules/comp/comp.test.ts +1 -1
- package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +74 -0
- package/src/lint/rules/comp/index.ts +3 -3
- package/src/lint/rules/op/index.ts +23 -0
- package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
- package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
- package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
- package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
- package/src/lint/rules/op/support.ts +11 -0
- package/src/lint/suppressions.test.ts +163 -0
- package/src/lint/suppressions.ts +204 -0
- package/src/live-endpoint.ts +1 -1
- package/src/op/activities/activity-contracts.ts +66 -0
- package/src/op/activities/apply.test.ts +567 -0
- package/src/op/activities/apply.ts +662 -0
- package/src/op/activities/build.ts +26 -0
- package/src/op/activities/converge.test.ts +330 -0
- package/src/op/activities/converge.ts +449 -0
- package/src/op/activities/env-teardown.test.ts +187 -0
- package/src/op/activities/env-teardown.ts +171 -0
- package/src/op/activities/guard-validate.test.ts +139 -0
- package/src/op/activities/guard-validate.ts +223 -0
- package/src/op/activities/http-check.test.ts +48 -0
- package/src/op/activities/http-check.ts +55 -0
- package/src/op/activities/index.ts +119 -0
- package/src/op/activities/lexicon-upgrade.test.ts +492 -0
- package/src/op/activities/lexicon-upgrade.ts +673 -0
- package/src/op/activities/lifecycle.ts +85 -0
- package/src/op/activities/pipeline-audit.test.ts +67 -0
- package/src/op/activities/pipeline-audit.ts +201 -0
- package/src/op/activities/policy.test.ts +19 -0
- package/src/op/activities/policy.ts +37 -0
- package/src/op/activities/reconcile.test.ts +81 -0
- package/src/op/activities/reconcile.ts +187 -0
- package/src/op/activities/shell.ts +26 -0
- package/src/op/activities/teardown.ts +22 -0
- package/src/op/activities/util.ts +6 -0
- package/src/op/activities/wait.ts +69 -0
- package/src/op/activities/workflow-audit.test.ts +90 -0
- package/src/op/activities/workflow-audit.ts +203 -0
- package/src/op/activity-contract-registry.test.ts +98 -0
- package/src/op/activity-contract-registry.ts +114 -0
- package/src/op/activity-contract.test.ts +1 -1
- package/src/op/activity-contract.ts +37 -33
- package/src/op/activity-failure.ts +34 -0
- package/src/op/activity-profiles.test.ts +56 -0
- package/src/op/activity-profiles.ts +116 -0
- package/src/op/activity-registry.test.ts +37 -28
- package/src/op/activity-registry.ts +27 -56
- package/src/op/activity-runtime.ts +3 -47
- package/src/op/builders-exports.test.ts +0 -49
- package/src/op/builders-typed.test.ts +164 -0
- package/src/op/builders.ts +123 -39
- package/src/op/composites/apply-op.ts +208 -0
- package/src/op/composites/composites.test.ts +366 -0
- package/src/op/composites/converge-op.test.ts +168 -0
- package/src/op/composites/converge-op.ts +214 -0
- package/src/op/composites/index.ts +26 -0
- package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
- package/src/op/composites/lexicon-upgrade-op.ts +134 -0
- package/src/op/composites/pipeline-audit-op.test.ts +25 -0
- package/src/op/composites/pipeline-audit-op.ts +78 -0
- package/src/op/composites/reconcile-op.test.ts +51 -0
- package/src/op/composites/reconcile-op.ts +107 -0
- package/src/op/composites/watch-op.ts +108 -0
- package/src/op/composites/workflow-audit-op.test.ts +25 -0
- package/src/op/composites/workflow-audit-op.ts +88 -0
- package/src/op/converge-rule.ts +12 -10
- package/src/op/cron.test.ts +128 -0
- package/src/op/cron.ts +149 -0
- package/src/op/discover-root.test.ts +105 -1
- package/src/op/discover.test.ts +6 -2
- package/src/op/discover.ts +84 -16
- package/src/op/duration.ts +21 -0
- package/src/op/effect-step.test.ts +26 -11
- package/src/op/emulator-lifecycle.ts +1 -2
- package/src/op/gate.ts +155 -0
- package/src/op/generate-pipeline.test.ts +65 -3
- package/src/op/generate-pipeline.ts +19 -2
- package/src/op/index.ts +39 -6
- package/src/op/local-executor.test.ts +163 -34
- package/src/op/local-executor.ts +370 -102
- package/src/op/local-output.test.ts +95 -11
- package/src/op/local-output.ts +33 -4
- package/src/op/op-ir.test.ts +281 -0
- package/src/op/op-ir.ts +417 -0
- package/src/op/op-verb-class.ts +19 -5
- package/src/op/op.test.ts +26 -5
- package/src/op/operator.test.ts +175 -6
- package/src/op/operator.ts +63 -16
- package/src/op/receipt-store.ts +1 -1
- package/src/op/resource.ts +20 -2
- package/src/op/runtime.ts +203 -0
- package/src/op/runtimes/local.test.ts +181 -0
- package/src/op/runtimes/local.ts +212 -0
- package/src/op/step-output-ref.ts +15 -13
- package/src/op/types.ts +84 -24
- package/src/ownership.ts +1 -1
- package/src/provenance.test.ts +6 -0
- package/src/provenance.ts +21 -1
- package/src/serializer-walker.test.ts +42 -0
- package/src/serializer-walker.ts +21 -2
- package/dist/cli/handlers/op-progress.d.ts +0 -90
- package/dist/cli/handlers/op-progress.d.ts.map +0 -1
- package/dist/cli/handlers/run-client.d.ts +0 -127
- package/dist/cli/handlers/run-client.d.ts.map +0 -1
- package/dist/cli/handlers/run-report.d.ts +0 -5
- package/dist/cli/handlers/run-report.d.ts.map +0 -1
- package/dist/components/temporal-codegen-loader.d.ts +0 -27
- package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
- package/src/cli/handlers/op-progress.test.ts +0 -268
- package/src/cli/handlers/op-progress.ts +0 -248
- package/src/cli/handlers/run-client.test.ts +0 -82
- package/src/cli/handlers/run-client.ts +0 -219
- package/src/cli/handlers/run-report.test.ts +0 -62
- package/src/cli/handlers/run-report.ts +0 -122
- package/src/components/temporal-codegen-loader.ts +0 -41
- package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { applyResult } from "../../apply";
|
|
3
|
+
import { defaultOutput, nativeApply, compensateApply, hasNativeRollback } from "./apply";
|
|
4
|
+
import type { K8sApplier, AzureApplier, GcpApplier, FlyApplier, AwsApplier, AwsRollback } from "./apply";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The kubectl branch moved to the k8s lexicon in chant #1075, the arm branch to
|
|
8
|
+
* the azure lexicon in #1448, and the cloudformation branch to the aws lexicon
|
|
9
|
+
* in #1449 — no shell target is left. What is asserted here is the dispatch —
|
|
10
|
+
* that a target never becomes a shell command again, that the arguments the
|
|
11
|
+
* composite emits reach each applier intact, and that every branch collapses
|
|
12
|
+
* onto the one normalized result (#1446 counts, collapsed in #1449). What the
|
|
13
|
+
* appliers *do* is their own lexicons' tests.
|
|
14
|
+
*/
|
|
15
|
+
describe("nativeApply: kustomize renders, then dispatches to the SAME k8s applier (#1548)", () => {
|
|
16
|
+
test("the rendered documents reach the applier inline, output as the render dir", async () => {
|
|
17
|
+
const calls: Parameters<K8sApplier>[0][] = [];
|
|
18
|
+
const k8s: K8sApplier = async (args) => {
|
|
19
|
+
calls.push(args);
|
|
20
|
+
return { applied: [{}], pruned: [], fieldManager: "chant:web" };
|
|
21
|
+
};
|
|
22
|
+
const rendered = [{ apiVersion: "v1", kind: "Namespace", metadata: { name: "web" } }];
|
|
23
|
+
const renderer = async (dir: string) => {
|
|
24
|
+
expect(dir).toBe("overlays/prod");
|
|
25
|
+
return rendered;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const result = await nativeApply(
|
|
29
|
+
{ target: "kustomize", env: "prod", output: "overlays/prod", deleteMode: "owned-only" },
|
|
30
|
+
undefined,
|
|
31
|
+
k8s,
|
|
32
|
+
undefined,
|
|
33
|
+
renderer,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
expect(calls).toHaveLength(1);
|
|
37
|
+
expect(calls[0].documents).toBe(rendered);
|
|
38
|
+
expect(calls[0].manifest).toBe("kustomize:overlays/prod"); // label, not a path
|
|
39
|
+
expect(calls[0].deleteMode).toBe("owned-only");
|
|
40
|
+
// The field manager stays on the log; the result is the collapsed counts.
|
|
41
|
+
expect(result).toEqual({ applied: 1, pruned: 0, notAttempted: 0 });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("defaultOutput: dist directory", async () => {
|
|
45
|
+
expect(defaultOutput("kustomize")).toBe("dist");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("nativeApply: kubectl dispatches to the k8s lexicon (chant #1075)", () => {
|
|
50
|
+
const applier = (): { fn: K8sApplier; calls: Parameters<K8sApplier>[0][] } => {
|
|
51
|
+
const calls: Parameters<K8sApplier>[0][] = [];
|
|
52
|
+
return {
|
|
53
|
+
calls,
|
|
54
|
+
fn: async (args) => {
|
|
55
|
+
calls.push(args);
|
|
56
|
+
return { applied: [{}, {}, {}], pruned: [{}], fieldManager: "chant:web" };
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
test("passes the manifest, environment and delete mode through, and reports what happened", async () => {
|
|
62
|
+
const k8s = applier();
|
|
63
|
+
const result = await nativeApply(
|
|
64
|
+
{ target: "kubectl", env: "prod", output: "dist", deleteMode: "owned-only" },
|
|
65
|
+
undefined,
|
|
66
|
+
k8s.fn,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(k8s.calls).toEqual([
|
|
70
|
+
{ manifest: "dist", environment: "prod", deleteMode: "owned-only" },
|
|
71
|
+
]);
|
|
72
|
+
// Counts preserved from the k8s applier's arrays; the k8s contract has no
|
|
73
|
+
// skip path (an unclassifiable document throws), so notAttempted is 0.
|
|
74
|
+
expect(result).toEqual({ applied: 3, pruned: 1, notAttempted: 0 });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("defaults the output to `dist` and the delete mode to never", async () => {
|
|
78
|
+
const k8s = applier();
|
|
79
|
+
await nativeApply({ target: "kubectl", env: "prod" }, undefined, k8s.fn);
|
|
80
|
+
expect(k8s.calls[0]).toMatchObject({ manifest: "dist", deleteMode: "never" });
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("force-conflicts is absent unless the caller asked for it", async () => {
|
|
84
|
+
const off = applier();
|
|
85
|
+
await nativeApply({ target: "kubectl", env: "prod" }, undefined, off.fn);
|
|
86
|
+
expect(off.calls[0].force).toBeUndefined();
|
|
87
|
+
|
|
88
|
+
const on = applier();
|
|
89
|
+
await nativeApply({ target: "kubectl", env: "prod", forceConflicts: true }, undefined, on.fn);
|
|
90
|
+
expect(on.calls[0].force).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("with nothing injected it resolves the real k8s lexicon's applyManifest", async () => {
|
|
94
|
+
// The delegation itself, not a stand-in for it. A manifest path that does
|
|
95
|
+
// not exist fails inside the k8s activity's own manifest read, which is
|
|
96
|
+
// only reachable if the dynamic import found the lexicon — and it fails
|
|
97
|
+
// before any connector runs, so nothing goes near a cluster.
|
|
98
|
+
const err = await nativeApply({
|
|
99
|
+
target: "kubectl",
|
|
100
|
+
env: "prod",
|
|
101
|
+
output: "/nonexistent/chant-1075-manifest",
|
|
102
|
+
}).catch((e: unknown) => e);
|
|
103
|
+
expect(String(err)).toMatch(/ENOENT|no such file/);
|
|
104
|
+
expect(String(err)).not.toMatch(/could not be loaded/);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("nativeApply: arm dispatches to the azure lexicon (chant #1448)", () => {
|
|
109
|
+
/** Records what the applier was handed, and reports an empty envelope. */
|
|
110
|
+
const spy = (): { calls: Array<Parameters<AzureApplier>[0]>; applier: AzureApplier } => {
|
|
111
|
+
const calls: Array<Parameters<AzureApplier>[0]> = [];
|
|
112
|
+
const applier: AzureApplier = async (args) => {
|
|
113
|
+
calls.push(args);
|
|
114
|
+
return applyResult([]);
|
|
115
|
+
};
|
|
116
|
+
return { calls, applier };
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// The fix. `owned-only` must reach azApply's marker-scoped prune, which filters
|
|
120
|
+
// on `isChantOwned(tags)` before issuing any delete — not ARM Complete mode,
|
|
121
|
+
// whose scope is the entire resource group.
|
|
122
|
+
test("owned-only asks the azure applier to prune, and issues no shell command", async () => {
|
|
123
|
+
const { calls, applier } = spy();
|
|
124
|
+
const result = await nativeApply(
|
|
125
|
+
{ target: "arm", env: "my-rg", output: "t.json", deleteMode: "owned-only" },
|
|
126
|
+
undefined,
|
|
127
|
+
undefined,
|
|
128
|
+
applier,
|
|
129
|
+
);
|
|
130
|
+
expect(calls).toHaveLength(1);
|
|
131
|
+
expect(calls[0].prune).toBe(true);
|
|
132
|
+
expect(calls[0].resourceGroup).toBe("my-rg");
|
|
133
|
+
expect(calls[0].templatePath).toBe("t.json");
|
|
134
|
+
expect(result).toEqual({ applied: 0, pruned: 0, notAttempted: 0 });
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("gated prunes too — same delete scope, the gate lives in the composite", async () => {
|
|
138
|
+
const { calls, applier } = spy();
|
|
139
|
+
await nativeApply({ target: "arm", env: "rg", deleteMode: "gated" }, undefined, undefined, applier);
|
|
140
|
+
expect(calls[0].prune).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("never does not prune", async () => {
|
|
144
|
+
const { calls, applier } = spy();
|
|
145
|
+
await nativeApply({ target: "arm", env: "rg", deleteMode: "never" }, undefined, undefined, applier);
|
|
146
|
+
expect(calls[0].prune).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("defaults to never when no delete mode is given", async () => {
|
|
150
|
+
const { calls, applier } = spy();
|
|
151
|
+
await nativeApply({ target: "arm", env: "rg" }, undefined, undefined, applier);
|
|
152
|
+
expect(calls[0].prune).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("defaults the template path the same way the other file targets do", async () => {
|
|
156
|
+
const { calls, applier } = spy();
|
|
157
|
+
await nativeApply({ target: "arm", env: "rg" }, undefined, undefined, applier);
|
|
158
|
+
expect(calls[0].templatePath).toBe("template.json");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Same variable azure's read path honours, so an apply lands wherever `--live`
|
|
162
|
+
// is already looking.
|
|
163
|
+
test("passes AZURE_ENDPOINT_URL through, and omits it when unset", async () => {
|
|
164
|
+
const prev = process.env.AZURE_ENDPOINT_URL;
|
|
165
|
+
try {
|
|
166
|
+
process.env.AZURE_ENDPOINT_URL = "http://localhost:4577";
|
|
167
|
+
const a = spy();
|
|
168
|
+
await nativeApply({ target: "arm", env: "rg" }, undefined, undefined, a.applier);
|
|
169
|
+
expect(a.calls[0].endpoint).toBe("http://localhost:4577");
|
|
170
|
+
|
|
171
|
+
delete process.env.AZURE_ENDPOINT_URL;
|
|
172
|
+
const b = spy();
|
|
173
|
+
await nativeApply({ target: "arm", env: "rg" }, undefined, undefined, b.applier);
|
|
174
|
+
expect(b.calls[0].endpoint).toBeUndefined();
|
|
175
|
+
} finally {
|
|
176
|
+
if (prev === undefined) delete process.env.AZURE_ENDPOINT_URL;
|
|
177
|
+
else process.env.AZURE_ENDPOINT_URL = prev;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("counts are preserved from the applier's envelope, not a command", async () => {
|
|
182
|
+
const applier: AzureApplier = async () =>
|
|
183
|
+
applyResult(
|
|
184
|
+
[
|
|
185
|
+
{ kind: "Microsoft.Storage/storageAccounts", name: "sa1", action: "updated" },
|
|
186
|
+
{ kind: "Microsoft.Network/virtualNetworks", name: "vnet1", action: "updated" },
|
|
187
|
+
],
|
|
188
|
+
[{ kind: "Microsoft.Storage/storageAccounts", name: "old", deleted: true }],
|
|
189
|
+
);
|
|
190
|
+
const result = await nativeApply({ target: "arm", env: "rg" }, undefined, undefined, applier);
|
|
191
|
+
expect(result).toEqual({ applied: 2, pruned: 1, notAttempted: 0 });
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("a not-prunable orphan (#1457) surfaces in the notAttempted count", async () => {
|
|
195
|
+
const applier: AzureApplier = async () =>
|
|
196
|
+
applyResult(
|
|
197
|
+
[{ kind: "Microsoft.Storage/storageAccounts", name: "sa1", action: "updated" }],
|
|
198
|
+
[],
|
|
199
|
+
[
|
|
200
|
+
{
|
|
201
|
+
kind: "Microsoft.Custom/widgets",
|
|
202
|
+
name: "w1",
|
|
203
|
+
reason: "not-prunable",
|
|
204
|
+
detail: "no-api-version",
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
);
|
|
208
|
+
const result = await nativeApply(
|
|
209
|
+
{ target: "arm", env: "rg", deleteMode: "owned-only" },
|
|
210
|
+
undefined,
|
|
211
|
+
undefined,
|
|
212
|
+
applier,
|
|
213
|
+
);
|
|
214
|
+
expect(result).toEqual({ applied: 1, pruned: 0, notAttempted: 1 });
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("with nothing injected it resolves the real azure lexicon's azApply", async () => {
|
|
218
|
+
// Same shape as the kubectl case above: point it at a closed local port so
|
|
219
|
+
// the failure comes from inside azApply's own transport — reachable only if
|
|
220
|
+
// the dynamic import found the lexicon — and never touches real Azure.
|
|
221
|
+
const prev = process.env.AZURE_ENDPOINT_URL;
|
|
222
|
+
process.env.AZURE_ENDPOINT_URL = "http://127.0.0.1:1";
|
|
223
|
+
try {
|
|
224
|
+
const err = await nativeApply({ target: "arm", env: "rg" }).catch((e: unknown) => e);
|
|
225
|
+
expect(String(err)).not.toMatch(/could not be loaded/);
|
|
226
|
+
expect(String(err)).not.toMatch(/predates chant/);
|
|
227
|
+
} finally {
|
|
228
|
+
if (prev === undefined) delete process.env.AZURE_ENDPOINT_URL;
|
|
229
|
+
else process.env.AZURE_ENDPOINT_URL = prev;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe("nativeApply: gcp dispatches to the gcp lexicon (chant #1449)", () => {
|
|
235
|
+
/** Records what the applier was handed, and reports an empty envelope. */
|
|
236
|
+
const spy = (): { calls: Array<Parameters<GcpApplier>[0]>; applier: GcpApplier } => {
|
|
237
|
+
const calls: Array<Parameters<GcpApplier>[0]> = [];
|
|
238
|
+
const applier: GcpApplier = async (args) => {
|
|
239
|
+
calls.push(args);
|
|
240
|
+
return applyResult([]);
|
|
241
|
+
};
|
|
242
|
+
return { calls, applier };
|
|
243
|
+
};
|
|
244
|
+
/** nativeApply with only the gcp applier injected. */
|
|
245
|
+
const applyGcp = (args: Parameters<typeof nativeApply>[0], applier: GcpApplier) =>
|
|
246
|
+
nativeApply(args, undefined, undefined, undefined, undefined, undefined, applier);
|
|
247
|
+
|
|
248
|
+
test("output maps to the manifest path, and nothing else is passed", async () => {
|
|
249
|
+
const { calls, applier } = spy();
|
|
250
|
+
await applyGcp({ target: "gcp", env: "prod", output: "dist/gcp.yaml" }, applier);
|
|
251
|
+
// Exactly the mapped pair. No `endpoint` — gcpApply resolves
|
|
252
|
+
// GCP_ENDPOINT_URL itself, the same variable its read path honours; no
|
|
253
|
+
// `project` — gcpApply resolves GOOGLE_CLOUD_PROJECT / the CNRM
|
|
254
|
+
// annotation itself; and no `env` — GCP has no stack or resource-group
|
|
255
|
+
// equivalent, so env is only a log label on this target.
|
|
256
|
+
expect(calls).toEqual([{ manifestPath: "dist/gcp.yaml", prune: false }]);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("owned-only asks the gcp applier to prune, and issues no shell command", async () => {
|
|
260
|
+
const { calls, applier } = spy();
|
|
261
|
+
await applyGcp({ target: "gcp", env: "prod", deleteMode: "owned-only" }, applier);
|
|
262
|
+
expect(calls[0].prune).toBe(true);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("gated prunes too — same delete scope, the gate lives in the composite", async () => {
|
|
266
|
+
const { calls, applier } = spy();
|
|
267
|
+
await applyGcp({ target: "gcp", env: "prod", deleteMode: "gated" }, applier);
|
|
268
|
+
expect(calls[0].prune).toBe(true);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("never (and the default) do not prune", async () => {
|
|
272
|
+
const explicit = spy();
|
|
273
|
+
await applyGcp({ target: "gcp", env: "prod", deleteMode: "never" }, explicit.applier);
|
|
274
|
+
expect(explicit.calls[0].prune).toBe(false);
|
|
275
|
+
|
|
276
|
+
const defaulted = spy();
|
|
277
|
+
await applyGcp({ target: "gcp", env: "prod" }, defaulted.applier);
|
|
278
|
+
expect(defaulted.calls[0].prune).toBe(false);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test("defaults the manifest path to dist/gcp.yaml", async () => {
|
|
282
|
+
const { calls, applier } = spy();
|
|
283
|
+
await applyGcp({ target: "gcp", env: "prod" }, applier);
|
|
284
|
+
expect(calls[0].manifestPath).toBe("dist/gcp.yaml");
|
|
285
|
+
expect(defaultOutput("gcp")).toBe("dist/gcp.yaml");
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("counts are preserved from the applier's envelope, skips included (#1447)", async () => {
|
|
289
|
+
// The skips of #1447 and a kind the prune could not consider both ride the
|
|
290
|
+
// envelope as NOT-ATTEMPTED entries — the old separate notPrunable count
|
|
291
|
+
// folds into the one notAttempted count.
|
|
292
|
+
const applier: GcpApplier = async () =>
|
|
293
|
+
applyResult(
|
|
294
|
+
[
|
|
295
|
+
{ kind: "StorageBucket", name: "b1", action: "created" },
|
|
296
|
+
{ kind: "StorageBucket", name: "b2", action: "unchanged" },
|
|
297
|
+
],
|
|
298
|
+
[{ kind: "StorageBucket", name: "old", deleted: true }],
|
|
299
|
+
[
|
|
300
|
+
{ kind: "PubSubTopic", name: "x", reason: "unsupported-kind" },
|
|
301
|
+
{ kind: "PubSubTopic", name: "*", reason: "not-prunable", detail: "no-list-capability" },
|
|
302
|
+
],
|
|
303
|
+
);
|
|
304
|
+
const result = await applyGcp({ target: "gcp", env: "prod" }, applier);
|
|
305
|
+
expect(result).toEqual({ applied: 2, pruned: 1, notAttempted: 2 });
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test("with nothing injected it resolves the real gcp lexicon's gcpApply", async () => {
|
|
309
|
+
// Same shape as the kubectl and cloudformation cases: a manifest path that
|
|
310
|
+
// does not exist fails inside gcpApply's own manifest read — reachable only
|
|
311
|
+
// if the dynamic import found the lexicon — and it fails before any HTTP
|
|
312
|
+
// call, so nothing goes near GCP or an emulator.
|
|
313
|
+
const err = await nativeApply({
|
|
314
|
+
target: "gcp",
|
|
315
|
+
env: "prod",
|
|
316
|
+
output: "/nonexistent/chant-1449-gcp.yaml",
|
|
317
|
+
}).catch((e: unknown) => e);
|
|
318
|
+
expect(String(err)).toMatch(/ENOENT|no such file/);
|
|
319
|
+
expect(String(err)).not.toMatch(/could not be loaded/);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
describe("nativeApply: fly dispatches to the fly lexicon (chant #1449)", () => {
|
|
324
|
+
/** Records what the applier was handed, and reports an empty envelope. */
|
|
325
|
+
const spy = (): { calls: Array<Parameters<FlyApplier>[0]>; applier: FlyApplier } => {
|
|
326
|
+
const calls: Array<Parameters<FlyApplier>[0]> = [];
|
|
327
|
+
const applier: FlyApplier = async (args) => {
|
|
328
|
+
calls.push(args);
|
|
329
|
+
return applyResult([]);
|
|
330
|
+
};
|
|
331
|
+
return { calls, applier };
|
|
332
|
+
};
|
|
333
|
+
/** nativeApply with only the fly applier injected. */
|
|
334
|
+
const applyFly = (args: Parameters<typeof nativeApply>[0], applier: FlyApplier) =>
|
|
335
|
+
nativeApply(args, undefined, undefined, undefined, undefined, undefined, undefined, applier);
|
|
336
|
+
|
|
337
|
+
test("output maps to the plan path, and nothing else is passed", async () => {
|
|
338
|
+
const { calls, applier } = spy();
|
|
339
|
+
await applyFly({ target: "fly", env: "prod", output: "dist/fly.json" }, applier);
|
|
340
|
+
// Exactly the mapped pair. No `endpoint` — flyApply resolves
|
|
341
|
+
// FLY_FLAPS_BASE_URL itself (mudflaps locally, real Fly when unset); no
|
|
342
|
+
// `token` — it resolves FLY_API_TOKEN itself; and no `env` — the app
|
|
343
|
+
// names live in the plan, so env is only a log label on this target.
|
|
344
|
+
expect(calls).toEqual([{ planPath: "dist/fly.json", prune: false }]);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test("owned-only asks the fly applier to prune, and issues no shell command", async () => {
|
|
348
|
+
const { calls, applier } = spy();
|
|
349
|
+
await applyFly({ target: "fly", env: "prod", deleteMode: "owned-only" }, applier);
|
|
350
|
+
expect(calls[0].prune).toBe(true);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("gated prunes too — same delete scope, the gate lives in the composite", async () => {
|
|
354
|
+
const { calls, applier } = spy();
|
|
355
|
+
await applyFly({ target: "fly", env: "prod", deleteMode: "gated" }, applier);
|
|
356
|
+
expect(calls[0].prune).toBe(true);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test("never (and the default) do not prune", async () => {
|
|
360
|
+
const explicit = spy();
|
|
361
|
+
await applyFly({ target: "fly", env: "prod", deleteMode: "never" }, explicit.applier);
|
|
362
|
+
expect(explicit.calls[0].prune).toBe(false);
|
|
363
|
+
|
|
364
|
+
const defaulted = spy();
|
|
365
|
+
await applyFly({ target: "fly", env: "prod" }, defaulted.applier);
|
|
366
|
+
expect(defaulted.calls[0].prune).toBe(false);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("defaults the plan path to dist/fly.json", async () => {
|
|
370
|
+
const { calls, applier } = spy();
|
|
371
|
+
await applyFly({ target: "fly", env: "prod" }, applier);
|
|
372
|
+
expect(calls[0].planPath).toBe("dist/fly.json");
|
|
373
|
+
expect(defaultOutput("fly")).toBe("dist/fly.json");
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("counts are preserved from the applier's envelope", async () => {
|
|
377
|
+
// The six applied classes and five pruned classes are flattened into the
|
|
378
|
+
// envelope by fly's own toApplyResult (tested in the fly lexicon); this
|
|
379
|
+
// dispatcher only carries the counts through.
|
|
380
|
+
const applier: FlyApplier = async () =>
|
|
381
|
+
applyResult(
|
|
382
|
+
[
|
|
383
|
+
{ kind: "app", name: "web", action: "created" },
|
|
384
|
+
{ kind: "machine", name: "web-1", action: "created" },
|
|
385
|
+
{ kind: "machine", name: "web-2", action: "updated" },
|
|
386
|
+
{ kind: "volume", name: "data", action: "unchanged" },
|
|
387
|
+
{ kind: "ip", name: "v4", action: "created" },
|
|
388
|
+
{ kind: "cert", name: "example.com", action: "created" },
|
|
389
|
+
{ kind: "secret", name: "API_KEY", action: "updated" },
|
|
390
|
+
],
|
|
391
|
+
[
|
|
392
|
+
{ kind: "machine", name: "old-1", deleted: true },
|
|
393
|
+
{ kind: "volume", name: "old-data", deleted: true },
|
|
394
|
+
{ kind: "ip", name: "1.2.3.4", deleted: true },
|
|
395
|
+
{ kind: "cert", name: "old.example.com", deleted: true },
|
|
396
|
+
{ kind: "secret", name: "OLD_KEY", deleted: true },
|
|
397
|
+
],
|
|
398
|
+
);
|
|
399
|
+
const result = await applyFly({ target: "fly", env: "prod" }, applier);
|
|
400
|
+
expect(result).toEqual({ applied: 7, pruned: 5, notAttempted: 0 });
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test("with nothing injected it resolves the real fly lexicon's flyApply", async () => {
|
|
404
|
+
// Same shape as the kubectl and cloudformation cases: a plan path that does
|
|
405
|
+
// not exist fails inside flyApply's own plan read — reachable only if the
|
|
406
|
+
// dynamic import found the lexicon — and it fails before any HTTP call, so
|
|
407
|
+
// nothing goes near Fly or an emulator.
|
|
408
|
+
const err = await nativeApply({
|
|
409
|
+
target: "fly",
|
|
410
|
+
env: "prod",
|
|
411
|
+
output: "/nonexistent/chant-1449-fly.json",
|
|
412
|
+
}).catch((e: unknown) => e);
|
|
413
|
+
expect(String(err)).toMatch(/ENOENT|no such file/);
|
|
414
|
+
expect(String(err)).not.toMatch(/could not be loaded/);
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
describe("nativeApply: cloudformation dispatches to the aws lexicon (chant #1449)", () => {
|
|
419
|
+
/** Records what the applier was handed, and reports a settled create. */
|
|
420
|
+
const spy = (): { calls: Array<Parameters<AwsApplier>[0]>; applier: AwsApplier } => {
|
|
421
|
+
const calls: Array<Parameters<AwsApplier>[0]> = [];
|
|
422
|
+
const applier: AwsApplier = async (args) => {
|
|
423
|
+
calls.push(args);
|
|
424
|
+
return { stackName: args.stackName, status: "CREATE_COMPLETE", action: "created" };
|
|
425
|
+
};
|
|
426
|
+
return { calls, applier };
|
|
427
|
+
};
|
|
428
|
+
/** nativeApply with only the aws applier injected. */
|
|
429
|
+
const applyCfn = (args: Parameters<typeof nativeApply>[0], applier: AwsApplier) =>
|
|
430
|
+
nativeApply(args, undefined, undefined, undefined, undefined, applier);
|
|
431
|
+
|
|
432
|
+
test("env maps to the stack name and output to the template path, and nothing else is passed", async () => {
|
|
433
|
+
const { calls, applier } = spy();
|
|
434
|
+
await applyCfn({ target: "cloudformation", env: "prod", output: "stack.json" }, applier);
|
|
435
|
+
// Exactly the mapped pair. No `capabilities` — awsApply derives them from
|
|
436
|
+
// the template body (#980, `awsDeployCapabilitiesForBody`); no `endpoint` —
|
|
437
|
+
// awsApply resolves AWS_ENDPOINT_URL[_CLOUDFORMATION] itself (#1694). Both
|
|
438
|
+
// rules used to be duplicated here as `cfnCapabilities`/`applyEndpoint`.
|
|
439
|
+
expect(calls).toEqual([{ templatePath: "stack.json", stackName: "prod" }]);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
test("defaults the template path to template.json", async () => {
|
|
443
|
+
const { calls, applier } = spy();
|
|
444
|
+
await applyCfn({ target: "cloudformation", env: "prod" }, applier);
|
|
445
|
+
expect(calls[0].templatePath).toBe("template.json");
|
|
446
|
+
expect(defaultOutput("cloudformation")).toBe("template.json");
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test("deleteMode changes nothing — the stack IS the ownership boundary", async () => {
|
|
450
|
+
// CFN removes resources dropped from the template within the stack itself,
|
|
451
|
+
// so owned-only and never hand the applier identical arguments.
|
|
452
|
+
const owned = spy();
|
|
453
|
+
await applyCfn({ target: "cloudformation", env: "prod", deleteMode: "owned-only" }, owned.applier);
|
|
454
|
+
const never = spy();
|
|
455
|
+
await applyCfn({ target: "cloudformation", env: "prod", deleteMode: "never" }, never.applier);
|
|
456
|
+
expect(owned.calls).toEqual(never.calls);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test("collapses onto the normalized counts — the stack is the unit", async () => {
|
|
460
|
+
// The stack name, settled status and action are the operator's detail and
|
|
461
|
+
// stay on the activity log; the result is the same count shape as every
|
|
462
|
+
// other target's. One settled deploy is one applied.
|
|
463
|
+
const applier: AwsApplier = async () => ({ stackName: "prod", status: "UPDATE_COMPLETE", action: "updated" });
|
|
464
|
+
const result = await applyCfn({ target: "cloudformation", env: "prod" }, applier);
|
|
465
|
+
expect(result).toEqual({ applied: 1, pruned: 0, notAttempted: 0 });
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test("with nothing injected it resolves the real aws lexicon's awsApply", async () => {
|
|
469
|
+
// Same shape as the kubectl case: a template path that does not exist fails
|
|
470
|
+
// inside awsApply's own template read — reachable only if the dynamic
|
|
471
|
+
// import found the lexicon — and it fails before any HTTP call, so nothing
|
|
472
|
+
// goes near AWS or an emulator.
|
|
473
|
+
const err = await nativeApply({
|
|
474
|
+
target: "cloudformation",
|
|
475
|
+
env: "prod",
|
|
476
|
+
output: "/nonexistent/chant-1449-template.json",
|
|
477
|
+
}).catch((e: unknown) => e);
|
|
478
|
+
expect(String(err)).toMatch(/ENOENT|no such file/);
|
|
479
|
+
expect(String(err)).not.toMatch(/could not be loaded/);
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
describe("compensateApply: cloudformation rolls back through the aws lexicon (chant #1449)", () => {
|
|
484
|
+
test("calls rollbackStack with the env as the stack name, and execs nothing", async () => {
|
|
485
|
+
const calls: Array<Parameters<AwsRollback>[0]> = [];
|
|
486
|
+
const rollback: AwsRollback = async (args) => {
|
|
487
|
+
calls.push(args);
|
|
488
|
+
return { stackName: args.stackName, rolledBack: true, status: "UPDATE_ROLLBACK_COMPLETE" };
|
|
489
|
+
};
|
|
490
|
+
const result = await compensateApply({ target: "cloudformation", env: "prod" }, undefined, rollback);
|
|
491
|
+
expect(calls).toEqual([{ stackName: "prod" }]);
|
|
492
|
+
expect(result).toEqual({ stackName: "prod", rolledBack: true, status: "UPDATE_ROLLBACK_COMPLETE" });
|
|
493
|
+
// A command in the result would mean the shell path ran.
|
|
494
|
+
expect(result.command).toBeUndefined();
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
test("a declined rollback (absent stack, or Floci's UnknownAction) is reported, not hidden", async () => {
|
|
498
|
+
const rollback: AwsRollback = async (args) => ({ stackName: args.stackName, rolledBack: false });
|
|
499
|
+
const result = await compensateApply({ target: "cloudformation", env: "prod" }, undefined, rollback);
|
|
500
|
+
expect(result).toEqual({ stackName: "prod", rolledBack: false });
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
test("an explicit command takes precedence over the native rollback", async () => {
|
|
504
|
+
let rolled = false;
|
|
505
|
+
const rollback: AwsRollback = async (args) => {
|
|
506
|
+
rolled = true;
|
|
507
|
+
return { stackName: args.stackName, rolledBack: true };
|
|
508
|
+
};
|
|
509
|
+
const result = await compensateApply(
|
|
510
|
+
{ target: "cloudformation", env: "prod", command: "echo custom-rollback" },
|
|
511
|
+
undefined,
|
|
512
|
+
rollback,
|
|
513
|
+
);
|
|
514
|
+
expect(rolled).toBe(false);
|
|
515
|
+
expect(result).toEqual({ command: "echo custom-rollback" });
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
test("kubectl / kustomize / arm / gcp / fly without a command throw — the defensive branch (#1449)", async () => {
|
|
519
|
+
// Unreachable from a built ApplyOp, which refuses this combination at
|
|
520
|
+
// build time. A hand-assembled op that reaches it fails loudly rather than
|
|
521
|
+
// returning a result that could read as a revert.
|
|
522
|
+
for (const target of ["kubectl", "kustomize", "arm", "gcp", "fly"] as const) {
|
|
523
|
+
const err = await compensateApply({ target, env: "prod" }).catch((e: unknown) => e);
|
|
524
|
+
expect(String(err)).toMatch(`no automatic rollback for target "${target}"`);
|
|
525
|
+
expect(String(err)).toMatch(/NOT\s+reverted/);
|
|
526
|
+
expect(String(err)).toMatch(/compensate\.command/);
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
test("hasNativeRollback: cloudformation only", () => {
|
|
531
|
+
expect(hasNativeRollback("cloudformation")).toBe(true);
|
|
532
|
+
for (const target of ["kubectl", "kustomize", "arm", "gcp", "fly"] as const) {
|
|
533
|
+
expect(hasNativeRollback(target)).toBe(false);
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
test("an explicit command still runs for targets without a native rollback", async () => {
|
|
538
|
+
const result = await compensateApply({ target: "arm", env: "rg", command: "echo arm-rollback" });
|
|
539
|
+
expect(result).toEqual({ command: "echo arm-rollback" });
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
test("with nothing injected it resolves the real aws lexicon's rollbackStack", async () => {
|
|
543
|
+
// Point the endpoint at a closed local port so the failure comes from inside
|
|
544
|
+
// rollbackStack's own transport — reachable only if the dynamic import found
|
|
545
|
+
// the lexicon — and never touches real AWS.
|
|
546
|
+
const prev = process.env.AWS_ENDPOINT_URL;
|
|
547
|
+
process.env.AWS_ENDPOINT_URL = "http://127.0.0.1:1";
|
|
548
|
+
try {
|
|
549
|
+
const err = await compensateApply({ target: "cloudformation", env: "prod" }).catch((e: unknown) => e);
|
|
550
|
+
expect(String(err)).not.toMatch(/could not be loaded/);
|
|
551
|
+
expect(String(err)).not.toMatch(/predates chant/);
|
|
552
|
+
} finally {
|
|
553
|
+
if (prev === undefined) delete process.env.AWS_ENDPOINT_URL;
|
|
554
|
+
else process.env.AWS_ENDPOINT_URL = prev;
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
describe('defaultOutput (target-aware apply output)', () => {
|
|
560
|
+
test('kubectl → dist (dir); cloudformation/arm → template.json (file); gcp → dist/gcp.yaml; fly → dist/fly.json', () => {
|
|
561
|
+
expect(defaultOutput('kubectl')).toBe('dist');
|
|
562
|
+
expect(defaultOutput('cloudformation')).toBe('template.json');
|
|
563
|
+
expect(defaultOutput('arm')).toBe('template.json');
|
|
564
|
+
expect(defaultOutput('gcp')).toBe('dist/gcp.yaml');
|
|
565
|
+
expect(defaultOutput('fly')).toBe('dist/fly.json');
|
|
566
|
+
});
|
|
567
|
+
});
|