@intentius/chant 0.57.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/catalog.d.ts +51 -1
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/core.d.ts +36 -1
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +26 -9
- 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/carve-bridge.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 +13 -0
- 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 +12 -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/plugins.d.ts +22 -0
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +40 -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 +24 -22
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +21 -6
- 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 +73 -37
- 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 +10 -22
- 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/detectLexicon.d.ts +13 -0
- package/dist/detectLexicon.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 +273 -16
- 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 +79 -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 +149 -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 +85 -30
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/change-signal.d.ts +91 -0
- package/dist/op/change-signal.d.ts.map +1 -0
- package/dist/op/composites/apply-op.d.ts +118 -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-name.d.ts +40 -0
- package/dist/op/gate-name.d.ts.map +1 -0
- package/dist/op/gate-summary.d.ts +43 -0
- package/dist/op/gate-summary.d.ts.map +1 -0
- 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 +29 -9
- 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 +185 -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 +93 -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 +96 -29
- 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/dist/terraform/bridge.d.ts +26 -7
- package/dist/terraform/bridge.d.ts.map +1 -1
- package/dist/terraform/carve-provider.d.ts +28 -2
- package/dist/terraform/carve-provider.d.ts.map +1 -1
- package/dist/terraform/data-source-shape.d.ts +77 -0
- package/dist/terraform/data-source-shape.d.ts.map +1 -0
- package/dist/terraform/graph.d.ts.map +1 -1
- package/dist/terraform/providers/kubernetes.d.ts +5 -0
- package/dist/terraform/providers/kubernetes.d.ts.map +1 -1
- package/dist/terraform/tier-map.d.ts +16 -4
- package/dist/terraform/tier-map.d.ts.map +1 -1
- package/dist/terraform/types.d.ts +8 -0
- package/dist/terraform/types.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 +84 -18
- package/src/audit/discover.test.ts +39 -0
- package/src/audit/discover.ts +202 -18
- 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 +191 -11
- package/src/cli/commands/audit.ts +78 -28
- package/src/cli/commands/build.test.ts +330 -9
- package/src/cli/commands/build.ts +170 -24
- package/src/cli/commands/carve-bridge.test.ts +237 -14
- package/src/cli/commands/carve-bridge.ts +21 -17
- package/src/cli/commands/carve-emit-k8s.test.ts +17 -10
- 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 +194 -1
- package/src/cli/commands/lint.ts +205 -7
- 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 +341 -55
- package/src/cli/handlers/run.test.ts +568 -1295
- package/src/cli/handlers/run.ts +446 -912
- package/src/cli/main.test.ts +22 -16
- package/src/cli/main.ts +66 -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/plugins.test.ts +68 -2
- package/src/cli/plugins.ts +39 -0
- package/src/cli/registry.ts +40 -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 +4 -3
- package/src/components/__fixtures__/neo4j-fanout.json +1 -1
- 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 +28 -12
- package/src/components/cli-support.ts +60 -47
- package/src/components/component-schema.test.ts +18 -2
- package/src/components/component.schema.json +18 -5
- package/src/components/component.test.ts +2 -2
- package/src/components/component.ts +27 -7
- package/src/components/config-defaults.test.ts +2 -2
- package/src/components/driver-output.ts +14 -2
- package/src/components/driver.test.ts +109 -20
- package/src/components/driver.ts +201 -84
- 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/neo4j-fanout.pilot.ts +3 -3
- 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.test.ts +19 -0
- package/src/components/verbs/run-agent.ts +12 -24
- 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/detectLexicon.ts +18 -1
- package/src/discovery/collect.test.ts +3 -3
- package/src/discovery/files.ts +4 -4
- package/src/discovery/fold-import.test.ts +1 -1
- 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 +82 -2
- 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 +3 -3
- 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 +296 -16
- 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 +154 -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/pipeline-change-gate.test.ts +2 -2
- 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 +27 -1
- package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +76 -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 +161 -0
- package/src/op/activities/reconcile.ts +354 -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 +125 -41
- package/src/op/change-signal.test.ts +117 -0
- package/src/op/change-signal.ts +169 -0
- package/src/op/composites/apply-op.ts +218 -0
- package/src/op/composites/composites.test.ts +379 -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 +110 -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 +29 -14
- package/src/op/emulator-lifecycle.ts +1 -2
- package/src/op/gate-name.test.ts +65 -0
- package/src/op/gate-name.ts +60 -0
- package/src/op/gate-summary.ts +84 -0
- 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 +48 -8
- package/src/op/local-executor.test.ts +178 -34
- package/src/op/local-executor.ts +371 -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 +292 -0
- package/src/op/op-ir.ts +419 -0
- package/src/op/op-verb-class.test.ts +2 -2
- package/src/op/op-verb-class.ts +19 -5
- package/src/op/op.test.ts +28 -7
- package/src/op/operator.test.ts +543 -6
- package/src/op/operator.ts +203 -20
- 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 +106 -26
- 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/src/terraform/aws-resources.test.ts +13 -4
- package/src/terraform/bridge.test.ts +22 -9
- package/src/terraform/bridge.ts +89 -28
- package/src/terraform/carve-provider.ts +38 -2
- package/src/terraform/data-source-shape.ts +95 -0
- package/src/terraform/graph.ts +35 -7
- package/src/terraform/providers/kubernetes.ts +48 -2
- package/src/terraform/tier-map.ts +21 -6
- package/src/terraform/types.ts +8 -0
- 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,354 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* What the reconcile activity does with the regenerated source.
|
|
9
|
+
*
|
|
10
|
+
* `comment` is the one mode that posts nothing new: it writes the body onto
|
|
11
|
+
* the pull request that triggered the run, updating the same comment on every
|
|
12
|
+
* re-run (chant #2231). It therefore needs a pull-request trigger, and
|
|
13
|
+
* {@link resolvePullRequestContext} fails the step by name when the run has
|
|
14
|
+
* none.
|
|
15
|
+
*/
|
|
16
|
+
export type ReconcileMode = "pull-request" | "issue" | "report" | "comment";
|
|
17
|
+
|
|
18
|
+
/** A change-set entry that triggered reconciliation. */
|
|
19
|
+
export interface ReconcileEntry {
|
|
20
|
+
/** chant entity name. */
|
|
21
|
+
name: string;
|
|
22
|
+
/** create | update | delete | adopt | noop (from `chant lifecycle plan`). */
|
|
23
|
+
action: string;
|
|
24
|
+
/** Resource type, when known. */
|
|
25
|
+
type?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ReconcilePrArgs {
|
|
29
|
+
/** Environment to reconcile from (passed to `chant import --from`). */
|
|
30
|
+
env: string;
|
|
31
|
+
/**
|
|
32
|
+
* The change-set entries that triggered this reconcile. Omit to derive them
|
|
33
|
+
* from `chant lifecycle plan <env> --json` at run time — the form used inside
|
|
34
|
+
* an Op, where the entries aren't known until the activity runs.
|
|
35
|
+
*/
|
|
36
|
+
entries?: ReconcileEntry[];
|
|
37
|
+
/** What to produce. Default: pull-request. */
|
|
38
|
+
mode?: ReconcileMode;
|
|
39
|
+
/** Output directory for regenerated source. Default: ./infra. */
|
|
40
|
+
output?: string;
|
|
41
|
+
/** Branch to open the PR from. Default: chant/reconcile-<env>. */
|
|
42
|
+
branch?: string;
|
|
43
|
+
/** Restrict live import to chant-owned resources. */
|
|
44
|
+
owned?: boolean;
|
|
45
|
+
/** PR / issue title. Default derived from env. */
|
|
46
|
+
title?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Hidden marker identifying this Op's comment on the pull request (comment
|
|
49
|
+
* mode). The activity writes it as the comment's first line and finds the
|
|
50
|
+
* comment again by it on the next run, so a re-run edits one comment instead
|
|
51
|
+
* of stacking a new one. Default: {@link commentMarker} keyed on `env`, so
|
|
52
|
+
* two Ops over two roots get two comments and each updates in place.
|
|
53
|
+
*/
|
|
54
|
+
marker?: string;
|
|
55
|
+
/**
|
|
56
|
+
* A finding body built by the caller, used verbatim as the issue/PR body in
|
|
57
|
+
* place of {@link reconcileSummary} (chant #2087).
|
|
58
|
+
*
|
|
59
|
+
* The summary this activity writes itself is a change-set table, which is
|
|
60
|
+
* the right artifact when the finding IS a change set. Some observe-dial
|
|
61
|
+
* Ops already hold a better one: `TerraformWatchOp` carries the
|
|
62
|
+
* `terraform plan -no-color` render its own Plan step produced, and
|
|
63
|
+
* re-deriving that from `chant lifecycle plan` here would both lose the
|
|
64
|
+
* plan and run a second, differently-timed read.
|
|
65
|
+
*
|
|
66
|
+
* Supplying it also suppresses the `chant lifecycle plan --json` derivation
|
|
67
|
+
* that fills `entries`: a caller that already knows what it wants to say
|
|
68
|
+
* is not asking this activity to go and find out.
|
|
69
|
+
*/
|
|
70
|
+
body?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface ReconcileResult {
|
|
74
|
+
mode: ReconcileMode;
|
|
75
|
+
/** Branch created (pull-request mode). */
|
|
76
|
+
branch?: string;
|
|
77
|
+
/** Opened PR URL (pull-request mode). */
|
|
78
|
+
prUrl?: string;
|
|
79
|
+
/** Opened issue URL (issue mode). */
|
|
80
|
+
issueUrl?: string;
|
|
81
|
+
/** The posted or updated PR comment's URL (comment mode). */
|
|
82
|
+
commentUrl?: string;
|
|
83
|
+
/** The pull request the comment landed on, `owner/repo#number` (comment mode). */
|
|
84
|
+
pullRequest?: string;
|
|
85
|
+
/** The markdown summary used as the PR/issue body. */
|
|
86
|
+
summary: string;
|
|
87
|
+
/** The entries that triggered the reconcile. */
|
|
88
|
+
entries: ReconcileEntry[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Default branch name for a reconcile PR. Deterministic — no timestamp. */
|
|
92
|
+
export function reconcileBranchName(env: string): string {
|
|
93
|
+
const safe = env.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
94
|
+
return `chant/reconcile-${safe}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Build the markdown body summarizing which change-set entries triggered the
|
|
99
|
+
* reconcile. Pure — used as the PR/issue body and returned in `report` mode.
|
|
100
|
+
*/
|
|
101
|
+
export function reconcileSummary(env: string, entries: ReconcileEntry[]): string {
|
|
102
|
+
const lines = [
|
|
103
|
+
`Reconcile from live environment \`${env}\`.`,
|
|
104
|
+
"",
|
|
105
|
+
"This PR regenerates chant TypeScript from live state to close the gap between the cloud and source. It was triggered by the following change-set entries:",
|
|
106
|
+
"",
|
|
107
|
+
"| Entry | Action | Type |",
|
|
108
|
+
"|---|---|---|",
|
|
109
|
+
];
|
|
110
|
+
for (const e of entries) {
|
|
111
|
+
lines.push(`| ${e.name} | ${e.action} | ${e.type ?? ""} |`);
|
|
112
|
+
}
|
|
113
|
+
if (entries.length === 0) {
|
|
114
|
+
lines.push("| _(none)_ | | |");
|
|
115
|
+
}
|
|
116
|
+
lines.push("");
|
|
117
|
+
lines.push("Review the diff before merging — live import may surface values that need redaction.");
|
|
118
|
+
return lines.join("\n");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function shellQuote(s: string): string {
|
|
122
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** The pull request a `comment`-mode run posts onto. */
|
|
126
|
+
export interface PullRequestContext {
|
|
127
|
+
/** `owner/repo`, from `GITHUB_REPOSITORY`. */
|
|
128
|
+
repo: string;
|
|
129
|
+
/** The pull request number. */
|
|
130
|
+
number: number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The hidden marker that makes a `comment`-mode finding findable across
|
|
135
|
+
* re-runs: written as the comment's first line, matched with `startswith` on
|
|
136
|
+
* the next run. Keyed on `env` (slugified the same way {@link
|
|
137
|
+
* reconcileBranchName} slugifies it, which also keeps the value free of the
|
|
138
|
+
* quotes and backslashes it is interpolated next to), so two Ops over two
|
|
139
|
+
* environments own two comments and each updates in place.
|
|
140
|
+
*/
|
|
141
|
+
export function commentMarker(env: string): string {
|
|
142
|
+
return `<!-- chant-reconcile:${env.replace(/[^a-zA-Z0-9._-]+/g, "-")} -->`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** What a `comment`-mode step says when the run it is in has no pull request. */
|
|
146
|
+
export function noPullRequestContextMessage(): string {
|
|
147
|
+
return (
|
|
148
|
+
'reconcilePr mode "comment" posts the finding on the pull request that triggered the run, and this run ' +
|
|
149
|
+
"has none. It needs GITHUB_REPOSITORY plus a pull request number, read from the event payload at " +
|
|
150
|
+
"GITHUB_EVENT_PATH (`.number` / `.pull_request.number`) or from GITHUB_REF (`refs/pull/<n>/merge`). " +
|
|
151
|
+
"GitHub Actions sets those on a pull_request event and on nothing else. Trigger this Op from a " +
|
|
152
|
+
'pull_request workflow, or give it findingMode "issue" or "report".'
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Read the pull request number out of a parsed webhook event payload. Pure.
|
|
158
|
+
* A `pull_request` event carries it top-level as `number` and again under
|
|
159
|
+
* `pull_request.number`; both are accepted, neither is invented.
|
|
160
|
+
*/
|
|
161
|
+
function prNumberFromPayload(payload: unknown): number | undefined {
|
|
162
|
+
if (typeof payload !== "object" || payload === null) return undefined;
|
|
163
|
+
const p = payload as { number?: unknown; pull_request?: { number?: unknown } };
|
|
164
|
+
if (typeof p.number === "number") return p.number;
|
|
165
|
+
if (typeof p.pull_request?.number === "number") return p.pull_request.number;
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Derive the triggering pull request from CI environment variables plus the
|
|
171
|
+
* already-parsed event payload. Pure — exported for testing; the IO (reading
|
|
172
|
+
* `GITHUB_EVENT_PATH`) is {@link resolvePullRequestContext}'s.
|
|
173
|
+
*
|
|
174
|
+
* Returns undefined rather than throwing, so the caller owns the message.
|
|
175
|
+
*/
|
|
176
|
+
export function pullRequestContextFrom(
|
|
177
|
+
env: Record<string, string | undefined>,
|
|
178
|
+
eventPayload?: unknown,
|
|
179
|
+
): PullRequestContext | undefined {
|
|
180
|
+
const repo = env.GITHUB_REPOSITORY;
|
|
181
|
+
if (!repo) return undefined;
|
|
182
|
+
const fromRef = /^refs\/pull\/(\d+)\//.exec(env.GITHUB_REF ?? "")?.[1];
|
|
183
|
+
const number = prNumberFromPayload(eventPayload) ?? (fromRef ? Number(fromRef) : undefined);
|
|
184
|
+
if (number === undefined || !Number.isInteger(number) || number <= 0) return undefined;
|
|
185
|
+
return { repo, number };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Resolve the triggering pull request, reading and parsing the event payload
|
|
190
|
+
* `GITHUB_EVENT_PATH` names. Throws {@link noPullRequestContextMessage} when
|
|
191
|
+
* the run has no pull request, which is the whole point: a `comment` mode that
|
|
192
|
+
* quietly fell back to an issue would post the finding somewhere nobody asked
|
|
193
|
+
* for it.
|
|
194
|
+
*/
|
|
195
|
+
export async function resolvePullRequestContext(
|
|
196
|
+
env: Record<string, string | undefined> = process.env,
|
|
197
|
+
): Promise<PullRequestContext> {
|
|
198
|
+
let payload: unknown;
|
|
199
|
+
const eventPath = env.GITHUB_EVENT_PATH;
|
|
200
|
+
if (eventPath) {
|
|
201
|
+
try {
|
|
202
|
+
payload = JSON.parse(await readFile(eventPath, "utf8"));
|
|
203
|
+
} catch {
|
|
204
|
+
// An unreadable or malformed payload is not fatal on its own: GITHUB_REF
|
|
205
|
+
// may still name the pull request. If it does not, the error below says so.
|
|
206
|
+
payload = undefined;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const ctx = pullRequestContextFrom(env, payload);
|
|
210
|
+
if (!ctx) throw new Error(noPullRequestContextMessage());
|
|
211
|
+
return ctx;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Post `body` as one comment on `ctx`'s pull request, or edit the comment this
|
|
216
|
+
* Op already owns there. The sticky-comment recipe the github lexicon's
|
|
217
|
+
* `PrPlanReport` uses, run from the activity instead of from generated YAML:
|
|
218
|
+
* find the comment whose body starts with `marker`, PATCH it when there is
|
|
219
|
+
* one, POST otherwise. `gh` ships on GitHub's hosted runners and is already
|
|
220
|
+
* this activity's dependency for the issue and pull-request modes, so the
|
|
221
|
+
* mode needs nothing new on the runner.
|
|
222
|
+
*/
|
|
223
|
+
async function postOrUpdateComment(
|
|
224
|
+
ctx: PullRequestContext,
|
|
225
|
+
marker: string,
|
|
226
|
+
body: string,
|
|
227
|
+
signal?: AbortSignal,
|
|
228
|
+
): Promise<string> {
|
|
229
|
+
const listPath = `repos/${ctx.repo}/issues/${ctx.number}/comments`;
|
|
230
|
+
const jq = `map(select(.body | startswith("${marker}"))) | .[0].id // empty`;
|
|
231
|
+
const { stdout: found } = await execAsync(
|
|
232
|
+
`gh api ${shellQuote(listPath)} --paginate --jq ${shellQuote(jq)}`,
|
|
233
|
+
{ signal },
|
|
234
|
+
);
|
|
235
|
+
// `--paginate` prints one `--jq` result per page, so take the first line
|
|
236
|
+
// that is an id and ignore the empty ones the other pages produce.
|
|
237
|
+
const existing = found.split("\n").map((l) => l.trim()).find((l) => /^\d+$/.test(l));
|
|
238
|
+
const field = `body=${marker}\n\n${body}`;
|
|
239
|
+
|
|
240
|
+
if (existing) {
|
|
241
|
+
const { stdout } = await execAsync(
|
|
242
|
+
`gh api --method PATCH ${shellQuote(`repos/${ctx.repo}/issues/comments/${existing}`)} ` +
|
|
243
|
+
`-f ${shellQuote(field)} --jq .html_url`,
|
|
244
|
+
{ signal },
|
|
245
|
+
);
|
|
246
|
+
return stdout.trim();
|
|
247
|
+
}
|
|
248
|
+
const { stdout } = await execAsync(
|
|
249
|
+
`gh api --method POST ${shellQuote(listPath)} -f ${shellQuote(field)} --jq .html_url`,
|
|
250
|
+
{ signal },
|
|
251
|
+
);
|
|
252
|
+
return stdout.trim();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Map a `chant lifecycle plan --json` ChangeSet to reconcile entries, dropping
|
|
257
|
+
* `noop` entries (nothing to reconcile). Pure — exported for testing.
|
|
258
|
+
*/
|
|
259
|
+
export function entriesFromPlan(planJson: string): ReconcileEntry[] {
|
|
260
|
+
const cs = JSON.parse(planJson) as {
|
|
261
|
+
entries?: Array<{ name: string; action: string; type?: string }>;
|
|
262
|
+
};
|
|
263
|
+
return (cs.entries ?? [])
|
|
264
|
+
.filter((e) => e.action !== "noop")
|
|
265
|
+
.map((e) => ({ name: e.name, action: e.action, type: e.type }));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Derive reconcile entries from `chant lifecycle plan`. */
|
|
269
|
+
async function derivePlanEntries(
|
|
270
|
+
env: string,
|
|
271
|
+
owned: boolean,
|
|
272
|
+
signal?: AbortSignal,
|
|
273
|
+
): Promise<ReconcileEntry[]> {
|
|
274
|
+
const ownedFlag = owned ? " --owned" : "";
|
|
275
|
+
const { stdout } = await execAsync(
|
|
276
|
+
`chant lifecycle plan ${shellQuote(env)}${ownedFlag} --json`,
|
|
277
|
+
{ signal },
|
|
278
|
+
);
|
|
279
|
+
return entriesFromPlan(stdout);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Reconcile activity: turn regenerated TypeScript into a reviewable artifact.
|
|
284
|
+
*
|
|
285
|
+
* - `report` — return the summary only; no git, no network.
|
|
286
|
+
* - `issue` — open a GitHub issue describing the drift (no code change).
|
|
287
|
+
* - `comment` — post the body as one comment on the pull request that
|
|
288
|
+
* triggered the run, editing that same comment on every re-run rather than
|
|
289
|
+
* stacking a new one (#2231). Needs a pull-request-triggered run; fails by
|
|
290
|
+
* name when there is none. No code change, and the `pull-requests: write`
|
|
291
|
+
* the generated workflow already grants on that trigger is the whole scope
|
|
292
|
+
* it spends.
|
|
293
|
+
* - `pull-request` — create a branch, regenerate source via
|
|
294
|
+
* `chant import --from <env>`, commit, push, and open a PR whose diff is the
|
|
295
|
+
* regenerated TypeScript. Never commits to the main branch.
|
|
296
|
+
*
|
|
297
|
+
* The body is {@link reconcileSummary}'s change-set table unless `args.body`
|
|
298
|
+
* supplies one, in which case that text is used verbatim and no plan is
|
|
299
|
+
* derived (chant #2087).
|
|
300
|
+
*
|
|
301
|
+
* Requires `chant` and (for non-report modes) `gh`/`git` in the environment.
|
|
302
|
+
*/
|
|
303
|
+
export async function reconcilePr(args: ReconcilePrArgs, signal?: AbortSignal): Promise<ReconcileResult> {
|
|
304
|
+
const mode = args.mode ?? "pull-request";
|
|
305
|
+
const owned = args.owned ?? false;
|
|
306
|
+
// A caller-supplied body means the finding is already written, so there is
|
|
307
|
+
// nothing for `chant lifecycle plan` to tell us (#2087).
|
|
308
|
+
const entries = args.entries ?? (args.body !== undefined ? [] : await derivePlanEntries(args.env, owned, signal));
|
|
309
|
+
const summary = args.body ?? reconcileSummary(args.env, entries);
|
|
310
|
+
const title = args.title ?? `Reconcile ${args.env}: ${entries.length} change(s) from live`;
|
|
311
|
+
|
|
312
|
+
if (mode === "report") {
|
|
313
|
+
return { mode, summary, entries };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (mode === "issue") {
|
|
317
|
+
const { stdout } = await execAsync(
|
|
318
|
+
`gh issue create --title ${shellQuote(title)} --body ${shellQuote(summary)}`,
|
|
319
|
+
{ signal },
|
|
320
|
+
);
|
|
321
|
+
return { mode, summary, entries, issueUrl: stdout.trim() };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (mode === "comment") {
|
|
325
|
+
// The trigger context is read here rather than passed in: a step's args
|
|
326
|
+
// are serialized at build time, and the pull request is not known until
|
|
327
|
+
// the run. Missing context is fatal — see `noPullRequestContextMessage`.
|
|
328
|
+
const ctx = await resolvePullRequestContext();
|
|
329
|
+
const marker = args.marker ?? commentMarker(args.env);
|
|
330
|
+
const commentUrl = await postOrUpdateComment(ctx, marker, summary, signal);
|
|
331
|
+
return { mode, summary, entries, commentUrl, pullRequest: `${ctx.repo}#${ctx.number}` };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// pull-request
|
|
335
|
+
const branch = args.branch ?? reconcileBranchName(args.env);
|
|
336
|
+
const output = args.output ?? "./infra";
|
|
337
|
+
const ownedFlag = owned ? " --owned" : "";
|
|
338
|
+
|
|
339
|
+
// Never touch the main branch: cut a fresh branch first.
|
|
340
|
+
await execAsync(`git checkout -b ${shellQuote(branch)}`, { signal });
|
|
341
|
+
await execAsync(
|
|
342
|
+
`chant import --from ${shellQuote(args.env)}${ownedFlag} --output ${shellQuote(output)} --force`,
|
|
343
|
+
{ signal },
|
|
344
|
+
);
|
|
345
|
+
await execAsync(`git add ${shellQuote(output)}`, { signal });
|
|
346
|
+
await execAsync(`git commit -m ${shellQuote(title)}`, { signal });
|
|
347
|
+
await execAsync(`git push -u origin ${shellQuote(branch)}`, { signal });
|
|
348
|
+
const { stdout } = await execAsync(
|
|
349
|
+
`gh pr create --title ${shellQuote(title)} --body ${shellQuote(summary)} --head ${shellQuote(branch)}`,
|
|
350
|
+
{ signal },
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
return { mode, branch, summary, entries, prUrl: stdout.trim() };
|
|
354
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
|
|
4
|
+
const execAsync = promisify(exec);
|
|
5
|
+
|
|
6
|
+
export interface ShellCmdArgs {
|
|
7
|
+
cmd: string;
|
|
8
|
+
/** Additional environment variables. */
|
|
9
|
+
env?: Record<string, string>;
|
|
10
|
+
/** Working directory. Default: process.cwd(). */
|
|
11
|
+
cwd?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Run an arbitrary shell command.
|
|
16
|
+
* Uses fastIdempotent profile — 5m timeout, 3 retries.
|
|
17
|
+
*/
|
|
18
|
+
export async function shellCmd(args: ShellCmdArgs, signal?: AbortSignal): Promise<string> {
|
|
19
|
+
const { stdout, stderr } = await execAsync(args.cmd, {
|
|
20
|
+
cwd: args.cwd,
|
|
21
|
+
env: { ...process.env, ...args.env },
|
|
22
|
+
signal,
|
|
23
|
+
});
|
|
24
|
+
if (stderr) console.error(stderr);
|
|
25
|
+
return stdout.trim();
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
|
|
4
|
+
const execAsync = promisify(exec);
|
|
5
|
+
|
|
6
|
+
export interface ChantTeardownArgs {
|
|
7
|
+
/** Path to the chant project to tear down. */
|
|
8
|
+
path: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Run `chant teardown` in the given project path.
|
|
13
|
+
* Uses longInfra profile — 20m timeout.
|
|
14
|
+
*/
|
|
15
|
+
export async function chantTeardown(args: ChantTeardownArgs, signal?: AbortSignal): Promise<void> {
|
|
16
|
+
const { stdout, stderr } = await execAsync("npm run teardown", {
|
|
17
|
+
cwd: args.path,
|
|
18
|
+
signal,
|
|
19
|
+
});
|
|
20
|
+
if (stdout) console.log(stdout);
|
|
21
|
+
if (stderr) console.error(stderr);
|
|
22
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { sleep } from "./util";
|
|
4
|
+
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
|
|
7
|
+
export interface WaitForStackArgs {
|
|
8
|
+
/** Stack name — used to locate the kubectl deployment/statefulset to poll. */
|
|
9
|
+
name: string;
|
|
10
|
+
/** Kubernetes namespace. */
|
|
11
|
+
namespace?: string;
|
|
12
|
+
/** kubectl context. */
|
|
13
|
+
context?: string;
|
|
14
|
+
/** Poll interval in ms. Default: 10000. */
|
|
15
|
+
intervalMs?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Poll until a Kubernetes Deployment or StatefulSet named `name` is fully rolled out.
|
|
20
|
+
* Uses the `k8sWait` profile — 15m timeout.
|
|
21
|
+
*
|
|
22
|
+
* Progress goes to stdout, one line per poll, which is the executor's step-record
|
|
23
|
+
* channel: the local executor streams an activity's output alongside the step it
|
|
24
|
+
* belongs to, so a long rollout is visible without a liveness protocol. Nothing
|
|
25
|
+
* mid-run is durable here by design (chant #2114) — the op re-runs and converges.
|
|
26
|
+
*
|
|
27
|
+
* Deliberately NOT migrated onto the generic `waitForReady` (#957): `kubectl
|
|
28
|
+
* rollout status` encodes rollout-specific semantics — progress-deadline
|
|
29
|
+
* timeouts, partial/paused rollouts, generation tracking — that a status-field
|
|
30
|
+
* predicate spec does not replicate, and it targets built-in workloads rather
|
|
31
|
+
* than arbitrary operator CRDs. `waitForReady` is the right tool for CRD
|
|
32
|
+
* readiness; `waitForStack` stays the right tool for workload rollouts.
|
|
33
|
+
*/
|
|
34
|
+
export async function waitForStack(args: WaitForStackArgs, signal?: AbortSignal): Promise<void> {
|
|
35
|
+
const ns = args.namespace ? `-n ${args.namespace}` : "";
|
|
36
|
+
const ctx = args.context ? `--context ${args.context}` : "";
|
|
37
|
+
const interval = args.intervalMs ?? 10_000;
|
|
38
|
+
let attempt = 0;
|
|
39
|
+
|
|
40
|
+
while (true) {
|
|
41
|
+
if (signal?.aborted) throw new Error("waitForStack aborted");
|
|
42
|
+
attempt++;
|
|
43
|
+
console.log(`[waitForStack] ${args.name}: poll ${attempt}`);
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
await execAsync(
|
|
47
|
+
`kubectl rollout status deployment/${args.name} ${ns} ${ctx} --timeout=30s`,
|
|
48
|
+
{ signal },
|
|
49
|
+
);
|
|
50
|
+
return;
|
|
51
|
+
} catch {
|
|
52
|
+
if (signal?.aborted) throw new Error("waitForStack aborted");
|
|
53
|
+
// Not ready yet — wait and retry
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
await execAsync(
|
|
58
|
+
`kubectl rollout status statefulset/${args.name} ${ns} ${ctx} --timeout=30s`,
|
|
59
|
+
{ signal },
|
|
60
|
+
);
|
|
61
|
+
return;
|
|
62
|
+
} catch {
|
|
63
|
+
if (signal?.aborted) throw new Error("waitForStack aborted");
|
|
64
|
+
// Not ready yet
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await sleep(interval, signal);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
workflowSupplyChainAudit,
|
|
4
|
+
collectAuditRefs,
|
|
5
|
+
type ActionRefResolution,
|
|
6
|
+
type ActionRefResolver,
|
|
7
|
+
} from "./workflow-audit";
|
|
8
|
+
|
|
9
|
+
const SHA = "1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b";
|
|
10
|
+
|
|
11
|
+
const WORKFLOW = `name: CI
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
jobs:
|
|
15
|
+
build:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@${SHA} # v4.1.0
|
|
19
|
+
- uses: evil/typo-action@v1
|
|
20
|
+
- uses: archived/old-action@v2
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
/** Recorded upstream responses — no live network. */
|
|
24
|
+
function recordedResolver(records: Record<string, ActionRefResolution>): ActionRefResolver {
|
|
25
|
+
return async (slug, ref) =>
|
|
26
|
+
records[`${slug}@${ref}`] ?? { exists: true, tags: [], archived: false, advisories: [] };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("collectAuditRefs (#292)", () => {
|
|
30
|
+
test("collects external refs with version comments, skipping local/docker", () => {
|
|
31
|
+
const yaml = `jobs:
|
|
32
|
+
a:
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@${SHA} # v4.1.0
|
|
35
|
+
- uses: ./.github/actions/local
|
|
36
|
+
- uses: docker://alpine:3.19
|
|
37
|
+
- uses: org/reusable/.github/workflows/x.yml@v1
|
|
38
|
+
`;
|
|
39
|
+
const refs = collectAuditRefs(yaml);
|
|
40
|
+
const slugs = refs.map((r) => r.slug);
|
|
41
|
+
expect(slugs).toContain("actions/checkout");
|
|
42
|
+
expect(slugs).toContain("org/reusable");
|
|
43
|
+
expect(slugs).not.toContain("./.github");
|
|
44
|
+
expect(refs.find((r) => r.slug === "actions/checkout")?.comment).toBe("v4.1.0");
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("workflowSupplyChainAudit (#292)", () => {
|
|
49
|
+
test("flags stale pin, impostor, archived, and pin/comment mismatch", async () => {
|
|
50
|
+
const resolver = recordedResolver({
|
|
51
|
+
// SHA no longer on any tag → stale; actual tag differs from comment → mismatch
|
|
52
|
+
[`actions/checkout@${SHA}`]: { exists: true, tags: ["v4.2.0"], archived: false, advisories: [] },
|
|
53
|
+
"evil/typo-action@v1": { exists: false, tags: [], archived: false, advisories: [] },
|
|
54
|
+
"archived/old-action@v2": { exists: true, tags: ["v2"], archived: true, advisories: ["GHSA-xxxx"] },
|
|
55
|
+
});
|
|
56
|
+
const result = await workflowSupplyChainAudit({ yaml: WORKFLOW, resolver, mode: "issue" });
|
|
57
|
+
const kinds = result.findings.map((f) => `${f.slug}:${f.kind}`);
|
|
58
|
+
|
|
59
|
+
expect(kinds).toContain("evil/typo-action:impostor");
|
|
60
|
+
expect(kinds).toContain("archived/old-action:archived");
|
|
61
|
+
expect(kinds).toContain("archived/old-action:advisory");
|
|
62
|
+
expect(kinds).toContain("actions/checkout:pin-comment-mismatch");
|
|
63
|
+
expect(result.mode).toBe("issue");
|
|
64
|
+
expect(result.summary).toContain("Workflow supply-chain audit");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("flags a stale SHA pin with no upstream tag", async () => {
|
|
68
|
+
const resolver = recordedResolver({
|
|
69
|
+
[`actions/checkout@${SHA}`]: { exists: true, tags: [], archived: false, advisories: [] },
|
|
70
|
+
"evil/typo-action@v1": { exists: true, tags: ["v1"], archived: false, advisories: [] },
|
|
71
|
+
"archived/old-action@v2": { exists: true, tags: ["v2"], archived: false, advisories: [] },
|
|
72
|
+
});
|
|
73
|
+
const result = await workflowSupplyChainAudit({ yaml: WORKFLOW, resolver });
|
|
74
|
+
expect(result.findings.some((f) => f.slug === "actions/checkout" && f.kind === "stale-pin")).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("clean workflow yields no findings", async () => {
|
|
78
|
+
const yaml = `jobs:
|
|
79
|
+
build:
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/checkout@${SHA}
|
|
82
|
+
`;
|
|
83
|
+
const resolver = recordedResolver({
|
|
84
|
+
[`actions/checkout@${SHA}`]: { exists: true, tags: ["v4"], archived: false, advisories: [] },
|
|
85
|
+
});
|
|
86
|
+
const result = await workflowSupplyChainAudit({ yaml, resolver });
|
|
87
|
+
expect(result.findings).toHaveLength(0);
|
|
88
|
+
expect(result.summary).toContain("No live drift");
|
|
89
|
+
});
|
|
90
|
+
});
|