@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,85 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
|
|
4
|
+
const execAsync = promisify(exec);
|
|
5
|
+
|
|
6
|
+
export interface LifecycleSnapshotArgs {
|
|
7
|
+
/** Environment name (e.g. "dev", "staging", "prod"). */
|
|
8
|
+
env: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Take a chant lifecycle snapshot for the given environment.
|
|
13
|
+
* Uses fastIdempotent profile — 5m timeout, 3 retries.
|
|
14
|
+
*/
|
|
15
|
+
export async function lifecycleSnapshot(args: LifecycleSnapshotArgs, signal?: AbortSignal): Promise<void> {
|
|
16
|
+
const { stdout, stderr } = await execAsync(`chant lifecycle snapshot ${args.env}`, { signal });
|
|
17
|
+
if (stdout) console.log(stdout);
|
|
18
|
+
if (stderr) console.error(stderr);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface LifecycleDiffArgs {
|
|
22
|
+
/** Environment name (e.g. "dev", "staging", "prod"). */
|
|
23
|
+
env: string;
|
|
24
|
+
/**
|
|
25
|
+
* When true, run `chant lifecycle diff <env> --live` (queries cloud APIs).
|
|
26
|
+
* When false (default), run digest-only diff against the last snapshot.
|
|
27
|
+
*/
|
|
28
|
+
live?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface LifecycleDiffResult {
|
|
32
|
+
/** Combined stdout + stderr from the chant command. */
|
|
33
|
+
output: string;
|
|
34
|
+
/** Process exit code (0 = success). */
|
|
35
|
+
exitCode: number;
|
|
36
|
+
/**
|
|
37
|
+
* True when the diff output contains any drift indicators
|
|
38
|
+
* (MISSING / ORPHAN / DRIFTED / DISAPPEARED section headers from
|
|
39
|
+
* `chant lifecycle diff --live`).
|
|
40
|
+
*/
|
|
41
|
+
drifted: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Section headers emitted by `chant lifecycle diff --live` that indicate a
|
|
46
|
+
* non-empty drift category. See packages/core/src/cli/handlers/state.ts.
|
|
47
|
+
*/
|
|
48
|
+
const DRIFT_HEADERS = [
|
|
49
|
+
"MISSING",
|
|
50
|
+
"ORPHAN",
|
|
51
|
+
"DISAPPEARED",
|
|
52
|
+
"DRIFTED",
|
|
53
|
+
"ARTIFACTS ADDED",
|
|
54
|
+
"ARTIFACTS REMOVED",
|
|
55
|
+
"ARTIFACTS CHANGED",
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
function detectDrift(output: string): boolean {
|
|
59
|
+
return DRIFT_HEADERS.some((h) => output.includes(`${h} (`) || output.includes(`\n${h}`));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Run `chant lifecycle diff <env>` and return the output + structured drift
|
|
64
|
+
* flag. Read-only; intended for use inside watch/observation Ops.
|
|
65
|
+
* Uses fastIdempotent profile.
|
|
66
|
+
*
|
|
67
|
+
* The `drifted` field is computed by scanning the output for any of the
|
|
68
|
+
* MISSING / ORPHAN / DRIFTED / DISAPPEARED section headers documented in
|
|
69
|
+
* cli/state.mdx. Pair with `outcomeAttribute: { name: "Drift", from: "drifted" }`
|
|
70
|
+
* on a WatchOp activity step to surface drift as the run's `Drift` outcome.
|
|
71
|
+
*/
|
|
72
|
+
export async function lifecycleDiff(args: LifecycleDiffArgs, signal?: AbortSignal): Promise<LifecycleDiffResult> {
|
|
73
|
+
const liveFlag = args.live ? " --live" : "";
|
|
74
|
+
try {
|
|
75
|
+
const { stdout, stderr } = await execAsync(`chant lifecycle diff ${args.env}${liveFlag}`, { signal });
|
|
76
|
+
const output = `${stdout}${stderr}`.trim();
|
|
77
|
+
if (output) console.log(output);
|
|
78
|
+
return { output, exitCode: 0, drifted: detectDrift(output) };
|
|
79
|
+
} catch (err) {
|
|
80
|
+
const e = err as { code?: number; stdout?: string; stderr?: string };
|
|
81
|
+
const output = `${e.stdout ?? ""}${e.stderr ?? ""}`.trim();
|
|
82
|
+
if (output) console.error(output);
|
|
83
|
+
return { output, exitCode: e.code ?? 1, drifted: detectDrift(output) };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
pipelineSupplyChainAudit,
|
|
4
|
+
collectPipelineRefs,
|
|
5
|
+
type PipelineRefResolution,
|
|
6
|
+
type GitlabRefResolver,
|
|
7
|
+
} from "./pipeline-audit";
|
|
8
|
+
|
|
9
|
+
const PIPELINE = `include:
|
|
10
|
+
- project: my-group/ci-templates
|
|
11
|
+
ref: v1.2.3
|
|
12
|
+
file: /build.yml
|
|
13
|
+
- component: gitlab.example.com/my-group/my-comp@1.0.0
|
|
14
|
+
|
|
15
|
+
build:
|
|
16
|
+
image:
|
|
17
|
+
name: node:20
|
|
18
|
+
script:
|
|
19
|
+
- npm ci
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
function recordedResolver(records: Record<string, PipelineRefResolution>): GitlabRefResolver {
|
|
23
|
+
return async (kind, identifier, ref) =>
|
|
24
|
+
records[`${kind}:${identifier}@${ref}`] ?? { exists: true, archived: false, advisories: [] };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe("collectPipelineRefs (#303)", () => {
|
|
28
|
+
test("collects include:project, component, and image refs", () => {
|
|
29
|
+
const refs = collectPipelineRefs(PIPELINE);
|
|
30
|
+
const ids = refs.map((r) => `${r.refKind}:${r.identifier}`);
|
|
31
|
+
expect(ids).toContain("include:my-group/ci-templates");
|
|
32
|
+
expect(ids).toContain("component:gitlab.example.com/my-group/my-comp");
|
|
33
|
+
expect(ids).toContain("image:node:20");
|
|
34
|
+
expect(refs.find((r) => r.refKind === "include")?.ref).toBe("v1.2.3");
|
|
35
|
+
expect(refs.find((r) => r.refKind === "component")?.ref).toBe("1.0.0");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("skips variable-based images", () => {
|
|
39
|
+
const refs = collectPipelineRefs(`build:\n image:\n name: $CI_REGISTRY_IMAGE:latest\n`);
|
|
40
|
+
expect(refs.filter((r) => r.refKind === "image")).toHaveLength(0);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("pipelineSupplyChainAudit (#303)", () => {
|
|
45
|
+
test("flags unresolved, archived, and moved references", async () => {
|
|
46
|
+
const resolver = recordedResolver({
|
|
47
|
+
"include:my-group/ci-templates@v1.2.3": { exists: true, archived: true, advisories: [] },
|
|
48
|
+
"component:gitlab.example.com/my-group/my-comp@1.0.0": { exists: false, archived: false, advisories: [] },
|
|
49
|
+
"image:node:20@20": { exists: true, archived: false, advisories: ["GHSA-img"], movedTo: undefined },
|
|
50
|
+
});
|
|
51
|
+
const result = await pipelineSupplyChainAudit({ yaml: PIPELINE, resolver, mode: "merge-request" });
|
|
52
|
+
const kinds = result.findings.map((f) => `${f.identifier}:${f.kind}`);
|
|
53
|
+
|
|
54
|
+
expect(kinds).toContain("my-group/ci-templates:archived");
|
|
55
|
+
expect(kinds).toContain("gitlab.example.com/my-group/my-comp:unresolved");
|
|
56
|
+
expect(kinds).toContain("node:20:advisory");
|
|
57
|
+
expect(result.mode).toBe("merge-request");
|
|
58
|
+
expect(result.summary).toContain("Pipeline include/component audit");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("clean pipeline yields no findings", async () => {
|
|
62
|
+
const resolver = recordedResolver({});
|
|
63
|
+
const result = await pipelineSupplyChainAudit({ yaml: PIPELINE, resolver });
|
|
64
|
+
expect(result.findings).toHaveLength(0);
|
|
65
|
+
expect(result.summary).toContain("No live drift");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pipelineSupplyChainAudit — the live counterpart to the gitlab post-synth
|
|
3
|
+
* include/component/image checks (#297).
|
|
4
|
+
*
|
|
5
|
+
* Some include/component checks can only be answered against a moving external
|
|
6
|
+
* truth: whether a pinned component/include ref still resolves upstream, whether
|
|
7
|
+
* the upstream project was archived or moved, or whether a new advisory now
|
|
8
|
+
* covers a component/image already in use. These change independent of the
|
|
9
|
+
* source, so they cannot live in the deterministic build — this activity owns
|
|
10
|
+
* *only* the checks that require live resolution.
|
|
11
|
+
*
|
|
12
|
+
* Dependency-free and primitives-only: it reads the emitted `.gitlab-ci.yml`
|
|
13
|
+
* (passed as a string) and resolves references through an injectable
|
|
14
|
+
* `GitlabRefResolver`, so `chant run` can schedule it and tests can run
|
|
15
|
+
* it against recorded responses with no live network.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** What the audit produces on findings. For GitLab the PR mode is a merge request. */
|
|
19
|
+
export type PipelineAuditMode = "report" | "issue" | "merge-request";
|
|
20
|
+
|
|
21
|
+
/** The kind of reference being resolved. */
|
|
22
|
+
export type PipelineRefKind = "include" | "component" | "image";
|
|
23
|
+
|
|
24
|
+
/** Live resolution of a single pipeline reference against its upstream. */
|
|
25
|
+
export interface PipelineRefResolution {
|
|
26
|
+
/** Does the project/component/image (at this ref/version) still resolve? */
|
|
27
|
+
exists: boolean;
|
|
28
|
+
/** Has the upstream project been archived? */
|
|
29
|
+
archived: boolean;
|
|
30
|
+
/** Has the upstream project moved (new path), if known. */
|
|
31
|
+
movedTo?: string;
|
|
32
|
+
/** Disclosed advisory identifiers affecting this reference. */
|
|
33
|
+
advisories: string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Pluggable upstream resolver — overridden in tests with recorded responses. */
|
|
37
|
+
export type GitlabRefResolver = (kind: PipelineRefKind, identifier: string, ref: string) => Promise<PipelineRefResolution>;
|
|
38
|
+
|
|
39
|
+
export type PipelineAuditFindingKind = "unresolved" | "archived" | "moved" | "advisory";
|
|
40
|
+
|
|
41
|
+
export interface PipelineAuditFinding {
|
|
42
|
+
kind: PipelineAuditFindingKind;
|
|
43
|
+
refKind: PipelineRefKind;
|
|
44
|
+
/** project path / component address / image. */
|
|
45
|
+
identifier: string;
|
|
46
|
+
/** ref / version / tag. */
|
|
47
|
+
ref: string;
|
|
48
|
+
detail: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface PipelineAuditArgs {
|
|
52
|
+
/** One emitted `.gitlab-ci.yml` document. */
|
|
53
|
+
yaml?: string;
|
|
54
|
+
/** Several emitted documents. */
|
|
55
|
+
yamls?: string[];
|
|
56
|
+
/** Path to an emitted `.gitlab-ci.yml` to read at run time (default `.gitlab-ci.yml`). */
|
|
57
|
+
pipelineFile?: string;
|
|
58
|
+
/** What to produce. Default: report. */
|
|
59
|
+
mode?: PipelineAuditMode;
|
|
60
|
+
/** Injectable upstream resolver. Defaults to the live GitLab REST resolver. */
|
|
61
|
+
resolver?: GitlabRefResolver;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface PipelineAuditResult {
|
|
65
|
+
mode: PipelineAuditMode;
|
|
66
|
+
findings: PipelineAuditFinding[];
|
|
67
|
+
summary: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface CollectedRef {
|
|
71
|
+
refKind: PipelineRefKind;
|
|
72
|
+
identifier: string;
|
|
73
|
+
ref: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Collect `include:project` (+ ref), `component:` (with @version), and `image:`
|
|
78
|
+
* references from a `.gitlab-ci.yml`.
|
|
79
|
+
*/
|
|
80
|
+
export function collectPipelineRefs(yaml: string): CollectedRef[] {
|
|
81
|
+
const out: CollectedRef[] = [];
|
|
82
|
+
|
|
83
|
+
// include:\n - project: group/proj\n ref: main
|
|
84
|
+
const projectRe = /-\s+project:\s*['"]?([^\s'"]+)['"]?[\s\S]*?ref:\s*['"]?([^\s'"]+)['"]?/g;
|
|
85
|
+
let m: RegExpExecArray | null;
|
|
86
|
+
while ((m = projectRe.exec(yaml)) !== null) {
|
|
87
|
+
out.push({ refKind: "include", identifier: m[1], ref: m[2] });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// - component: host/group/comp@1.0.0
|
|
91
|
+
const componentRe = /-\s+component:\s*['"]?([^\s'"@]+)@([^\s'"]+)['"]?/g;
|
|
92
|
+
while ((m = componentRe.exec(yaml)) !== null) {
|
|
93
|
+
out.push({ refKind: "component", identifier: m[1], ref: m[2] });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Images: inline `image: name` or block `image:\n name: ...` (and services).
|
|
97
|
+
const lines = yaml.split("\n");
|
|
98
|
+
const imgSeen = new Set<string>();
|
|
99
|
+
const addImage = (raw: string) => {
|
|
100
|
+
const image = raw.trim().replace(/^['"]|['"]$/g, "");
|
|
101
|
+
if (!image || image.includes("$") || imgSeen.has(image)) return;
|
|
102
|
+
imgSeen.add(image);
|
|
103
|
+
const at = image.lastIndexOf("@");
|
|
104
|
+
const colon = image.lastIndexOf(":");
|
|
105
|
+
const ref = at !== -1 ? image.slice(at + 1) : colon > image.lastIndexOf("/") ? image.slice(colon + 1) : "latest";
|
|
106
|
+
out.push({ refKind: "image", identifier: image, ref });
|
|
107
|
+
};
|
|
108
|
+
for (let i = 0; i < lines.length; i++) {
|
|
109
|
+
const inline = lines[i].match(/^\s+image:[ \t]+(\S.*)$/);
|
|
110
|
+
if (inline) { addImage(inline[1]); continue; }
|
|
111
|
+
if (/^\s+image:\s*$/.test(lines[i]) || /^\s+-\s+name:[ \t]+(\S.*)$/.test(lines[i])) {
|
|
112
|
+
const svcName = lines[i].match(/^\s+-\s+name:[ \t]+(\S.*)$/);
|
|
113
|
+
if (svcName) { addImage(svcName[1]); continue; }
|
|
114
|
+
const nameLine = lines.slice(i + 1, i + 4).find((l) => /^\s+name:[ \t]+/.test(l));
|
|
115
|
+
if (nameLine) addImage(nameLine.replace(/^\s+name:[ \t]+/, ""));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Default resolver: query the GitLab REST API for project existence / archive /
|
|
124
|
+
* move status. Best-effort and resilient — errors yield an "unknown" resolution
|
|
125
|
+
* (exists=true, no findings) rather than a false positive. Component/image
|
|
126
|
+
* advisory lookups are left to a configured resolver.
|
|
127
|
+
*/
|
|
128
|
+
export const defaultGitlabRefResolver: GitlabRefResolver = async (kind, identifier) => {
|
|
129
|
+
const unknown: PipelineRefResolution = { exists: true, archived: false, advisories: [] };
|
|
130
|
+
if (kind === "image") return unknown; // image advisory feeds are resolver-specific
|
|
131
|
+
const host = "https://gitlab.com";
|
|
132
|
+
const project = kind === "component" ? identifier.split("/").slice(1, 3).join("/") : identifier;
|
|
133
|
+
try {
|
|
134
|
+
const resp = await fetch(`${host}/api/v4/projects/${encodeURIComponent(project)}`, {
|
|
135
|
+
headers: process.env.GITLAB_TOKEN ? { "PRIVATE-TOKEN": process.env.GITLAB_TOKEN } : {},
|
|
136
|
+
});
|
|
137
|
+
if (resp.status === 404) return { exists: false, archived: false, advisories: [] };
|
|
138
|
+
if (!resp.ok) return unknown;
|
|
139
|
+
const p = (await resp.json()) as { archived?: boolean };
|
|
140
|
+
return { exists: true, archived: !!p.archived, advisories: [] };
|
|
141
|
+
} catch {
|
|
142
|
+
return unknown;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
function renderSummary(findings: PipelineAuditFinding[]): string {
|
|
147
|
+
if (findings.length === 0) return "## Pipeline include/component audit\n\nNo live drift detected against upstream references.\n";
|
|
148
|
+
let out = `## Pipeline include/component audit\n\n${findings.length} finding(s) against live upstream truth:\n\n`;
|
|
149
|
+
out += "| Reference | Ref | Finding | Detail |\n|---|---|---|---|\n";
|
|
150
|
+
for (const f of findings) out += `| ${f.identifier} (${f.refKind}) | ${f.ref} | ${f.kind} | ${f.detail} |\n`;
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function readFileBestEffort(path: string): Promise<string[]> {
|
|
155
|
+
try {
|
|
156
|
+
const { readFile } = await import("node:fs/promises");
|
|
157
|
+
return [await readFile(path, "utf-8")];
|
|
158
|
+
} catch {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Resolve each include/component/image reference in the emitted pipeline against
|
|
165
|
+
* live upstreams and report drift.
|
|
166
|
+
*/
|
|
167
|
+
export async function pipelineSupplyChainAudit(args: PipelineAuditArgs): Promise<PipelineAuditResult> {
|
|
168
|
+
const resolver = args.resolver ?? defaultGitlabRefResolver;
|
|
169
|
+
const mode = args.mode ?? "report";
|
|
170
|
+
let yamls = args.yamls ?? (args.yaml ? [args.yaml] : []);
|
|
171
|
+
if (yamls.length === 0) {
|
|
172
|
+
yamls = await readFileBestEffort(args.pipelineFile ?? ".gitlab-ci.yml");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const findings: PipelineAuditFinding[] = [];
|
|
176
|
+
const seen = new Set<string>();
|
|
177
|
+
for (const yaml of yamls) {
|
|
178
|
+
for (const { refKind, identifier, ref } of collectPipelineRefs(yaml)) {
|
|
179
|
+
const key = `${refKind}:${identifier}@${ref}`;
|
|
180
|
+
if (seen.has(key)) continue;
|
|
181
|
+
seen.add(key);
|
|
182
|
+
|
|
183
|
+
const res = await resolver(refKind, identifier, ref);
|
|
184
|
+
if (!res.exists) {
|
|
185
|
+
findings.push({ kind: "unresolved", refKind, identifier, ref, detail: `${refKind} "${identifier}@${ref}" no longer resolves upstream` });
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (res.archived) {
|
|
189
|
+
findings.push({ kind: "archived", refKind, identifier, ref, detail: `${identifier} has been archived upstream` });
|
|
190
|
+
}
|
|
191
|
+
if (res.movedTo) {
|
|
192
|
+
findings.push({ kind: "moved", refKind, identifier, ref, detail: `${identifier} moved to ${res.movedTo}` });
|
|
193
|
+
}
|
|
194
|
+
for (const adv of res.advisories) {
|
|
195
|
+
findings.push({ kind: "advisory", refKind, identifier, ref, detail: `disclosed advisory ${adv} covers ${identifier}` });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return { mode, findings, summary: renderSummary(findings) };
|
|
201
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { policyGate } from "./policy";
|
|
4
|
+
|
|
5
|
+
// The #201 org-policy example: a compliant workload + a policy pack
|
|
6
|
+
// (ORG-COST-CENTER all-envs, ORG-PROD-TLS prod-only). policyGate builds it and
|
|
7
|
+
// runs the policies — passing in dev, blocking in prod.
|
|
8
|
+
const orgPolicySrc = resolve(import.meta.dirname, "../../../../../lexicons/k8s/examples/org-policy/src");
|
|
9
|
+
|
|
10
|
+
describe("policyGate activity (#201 T4 — gate an apply on policy)", () => {
|
|
11
|
+
test("passes when policy is satisfied (no env, and dev)", async () => {
|
|
12
|
+
await expect(policyGate({ path: orgPolicySrc })).resolves.toBeUndefined();
|
|
13
|
+
await expect(policyGate({ path: orgPolicySrc, env: "dev" })).resolves.toBeUndefined();
|
|
14
|
+
}, 30_000);
|
|
15
|
+
|
|
16
|
+
test("blocks the apply on a prod policy violation", async () => {
|
|
17
|
+
await expect(policyGate({ path: orgPolicySrc, env: "prod" })).rejects.toThrow(/ORG-PROD-TLS/);
|
|
18
|
+
}, 30_000);
|
|
19
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { nonRetryableFailure } from "../activity-failure";
|
|
2
|
+
import { evaluateProjectPolicies } from "../../lint/policy";
|
|
3
|
+
|
|
4
|
+
export interface PolicyGateArgs {
|
|
5
|
+
/** Project/source directory to build and evaluate. Default ".". */
|
|
6
|
+
path?: string;
|
|
7
|
+
/** Environment for policy evaluation (falls back to `ownership.env`). */
|
|
8
|
+
env?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Gate an apply on organizational policy: build the project, run its
|
|
13
|
+
* `lint.policies` over the resolved resources, and **block** on any violation.
|
|
14
|
+
* Place it as a step before the apply phase — a violation fails the run
|
|
15
|
+
* (non-retryable; the `policyCheck` profile is single-attempt) so nothing is
|
|
16
|
+
* applied. A clean evaluation passes through.
|
|
17
|
+
*
|
|
18
|
+
* A plain activity on every runtime, not a `gate` step.
|
|
19
|
+
*/
|
|
20
|
+
export async function policyGate(args: PolicyGateArgs, _signal?: AbortSignal): Promise<void> {
|
|
21
|
+
const { violations, suppressed, env } = await evaluateProjectPolicies({ path: args.path ?? ".", env: args.env });
|
|
22
|
+
if (violations.length > 0) {
|
|
23
|
+
const summary = violations
|
|
24
|
+
.map((v) => `[${v.checkId}]${v.entity ? ` ${v.entity}:` : ""} ${v.message}`)
|
|
25
|
+
.join("; ");
|
|
26
|
+
throw nonRetryableFailure(
|
|
27
|
+
`Organizational policy blocked the apply — ${violations.length} violation(s): ${summary}`,
|
|
28
|
+
"PolicyViolation",
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
// chant #1138 — a check `lint.rules` turned "off" is counted here rather
|
|
32
|
+
// than vanishing silently, mirroring `chant build`'s own suppressed-count line.
|
|
33
|
+
if (suppressed.length > 0) {
|
|
34
|
+
console.log(`[policy] ${suppressed.length} finding(s) suppressed via lint.rules`);
|
|
35
|
+
}
|
|
36
|
+
console.log(`[policy] ${env ? `env=${env}: ` : ""}no violations — apply may proceed`);
|
|
37
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { describe, test, expect, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
reconcilePr,
|
|
4
|
+
reconcileSummary,
|
|
5
|
+
reconcileBranchName,
|
|
6
|
+
entriesFromPlan,
|
|
7
|
+
commentMarker,
|
|
8
|
+
pullRequestContextFrom,
|
|
9
|
+
} from "./reconcile";
|
|
10
|
+
|
|
11
|
+
const entries = [
|
|
12
|
+
{ name: "bucket", action: "adopt", type: "AWS::S3::Bucket" },
|
|
13
|
+
{ name: "queue", action: "update", type: "AWS::SQS::Queue" },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
describe("reconcileBranchName (#122)", () => {
|
|
17
|
+
test("deterministic, slugified per env", () => {
|
|
18
|
+
expect(reconcileBranchName("prod")).toBe("chant/reconcile-prod");
|
|
19
|
+
expect(reconcileBranchName("us-east/1")).toBe("chant/reconcile-us-east-1");
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("reconcileSummary (#122)", () => {
|
|
24
|
+
test("summarizes which entries triggered the reconcile", () => {
|
|
25
|
+
const body = reconcileSummary("prod", entries);
|
|
26
|
+
expect(body).toContain("live environment `prod`");
|
|
27
|
+
expect(body).toContain("| bucket | adopt | AWS::S3::Bucket |");
|
|
28
|
+
expect(body).toContain("| queue | update | AWS::SQS::Queue |");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("handles an empty entry set", () => {
|
|
32
|
+
expect(reconcileSummary("prod", [])).toContain("_(none)_");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("entriesFromPlan (#123)", () => {
|
|
37
|
+
test("maps a ChangeSet, dropping noop entries", () => {
|
|
38
|
+
const plan = JSON.stringify({
|
|
39
|
+
env: "prod",
|
|
40
|
+
entries: [
|
|
41
|
+
{ name: "a", action: "create", type: "T1", evidence: {}, ownership: "unknown" },
|
|
42
|
+
{ name: "b", action: "noop", type: "T2", evidence: {}, ownership: "unknown" },
|
|
43
|
+
{ name: "c", action: "delete", type: "T3", evidence: {}, ownership: "owned" },
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
expect(entriesFromPlan(plan)).toEqual([
|
|
47
|
+
{ name: "a", action: "create", type: "T1" },
|
|
48
|
+
{ name: "c", action: "delete", type: "T3" },
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("tolerates an empty / entry-less plan", () => {
|
|
53
|
+
expect(entriesFromPlan(JSON.stringify({ env: "prod" }))).toEqual([]);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("reconcilePr report mode (#122)", () => {
|
|
58
|
+
test("returns the summary without any git/network IO", async () => {
|
|
59
|
+
const result = await reconcilePr({ env: "prod", entries, mode: "report" });
|
|
60
|
+
expect(result.mode).toBe("report");
|
|
61
|
+
expect(result.prUrl).toBeUndefined();
|
|
62
|
+
expect(result.branch).toBeUndefined();
|
|
63
|
+
expect(result.summary).toContain("| bucket | adopt | AWS::S3::Bucket |");
|
|
64
|
+
expect(result.entries).toEqual(entries);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("reconcilePr pre-built body (#2087)", () => {
|
|
69
|
+
test("a caller-supplied body is used verbatim, in place of the change-set table", async () => {
|
|
70
|
+
const plan = "Terraform will perform the following actions:\n\n # null_resource.first will be created";
|
|
71
|
+
const result = await reconcilePr({ env: "app", mode: "report", body: plan });
|
|
72
|
+
expect(result.summary).toBe(plan);
|
|
73
|
+
expect(result.summary).not.toContain("| Entry | Action | Type |");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("supplying a body derives no plan, so nothing shells to `chant lifecycle plan`", async () => {
|
|
77
|
+
// No `entries`, no mock, no network: if the derivation still ran this
|
|
78
|
+
// would spawn `chant lifecycle plan --json` and reject.
|
|
79
|
+
const result = await reconcilePr({ env: "app", mode: "report", body: "drift" });
|
|
80
|
+
expect(result.entries).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("explicit entries still ride alongside a supplied body", async () => {
|
|
84
|
+
const result = await reconcilePr({ env: "prod", mode: "report", entries, body: "drift" });
|
|
85
|
+
expect(result.summary).toBe("drift");
|
|
86
|
+
expect(result.entries).toEqual(entries);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("reconcilePr comment mode: the marker (#2231)", () => {
|
|
91
|
+
test("the marker is deterministic per env, so a re-run finds its own comment", () => {
|
|
92
|
+
expect(commentMarker("app")).toBe("<!-- chant-reconcile:app -->");
|
|
93
|
+
expect(commentMarker("app")).toBe(commentMarker("app"));
|
|
94
|
+
expect(commentMarker("app")).not.toBe(commentMarker("db"));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("the marker slugifies the env, so nothing it is interpolated next to can be escaped out of", () => {
|
|
98
|
+
// The marker is interpolated into a jq `startswith("…")` string and into a
|
|
99
|
+
// shell word. A quote or a backslash surviving into it would break both.
|
|
100
|
+
const marker = commentMarker('us-east/1" or true; #');
|
|
101
|
+
expect(marker).toBe("<!-- chant-reconcile:us-east-1-or-true- -->");
|
|
102
|
+
expect(marker).not.toMatch(/["'\\]/);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("pullRequestContextFrom (#2231)", () => {
|
|
107
|
+
const repo = "INTENTIUS/chant";
|
|
108
|
+
|
|
109
|
+
test("reads the number off a pull_request event payload", () => {
|
|
110
|
+
expect(pullRequestContextFrom({ GITHUB_REPOSITORY: repo }, { number: 2231 })).toEqual({
|
|
111
|
+
repo,
|
|
112
|
+
number: 2231,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("accepts the nested pull_request.number the same payload also carries", () => {
|
|
117
|
+
expect(
|
|
118
|
+
pullRequestContextFrom({ GITHUB_REPOSITORY: repo }, { pull_request: { number: 7 } }),
|
|
119
|
+
).toEqual({ repo, number: 7 });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("falls back to GITHUB_REF when the payload is unreadable", () => {
|
|
123
|
+
expect(
|
|
124
|
+
pullRequestContextFrom({ GITHUB_REPOSITORY: repo, GITHUB_REF: "refs/pull/42/merge" }),
|
|
125
|
+
).toEqual({ repo, number: 42 });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("a push run has no pull request", () => {
|
|
129
|
+
expect(
|
|
130
|
+
pullRequestContextFrom(
|
|
131
|
+
{ GITHUB_REPOSITORY: repo, GITHUB_REF: "refs/heads/main" },
|
|
132
|
+
{ ref: "refs/heads/main", after: "abc" },
|
|
133
|
+
),
|
|
134
|
+
).toBeUndefined();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("a cron run off any forge has neither variable", () => {
|
|
138
|
+
expect(pullRequestContextFrom({})).toBeUndefined();
|
|
139
|
+
expect(pullRequestContextFrom({ GITHUB_REF: "refs/pull/1/merge" })).toBeUndefined();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("reconcilePr comment mode refuses a run with no pull request (#2231)", () => {
|
|
144
|
+
test("the message names the mode and every variable it looked for", async () => {
|
|
145
|
+
vi.stubEnv("GITHUB_REPOSITORY", "");
|
|
146
|
+
vi.stubEnv("GITHUB_REF", "");
|
|
147
|
+
vi.stubEnv("GITHUB_EVENT_PATH", "");
|
|
148
|
+
try {
|
|
149
|
+
// No `gh` is ever reached: the context check runs first, so a failure
|
|
150
|
+
// here is the refusal rather than a missing binary.
|
|
151
|
+
await expect(reconcilePr({ env: "app", mode: "comment", body: "plan" })).rejects.toThrow(
|
|
152
|
+
/mode "comment".*GITHUB_REPOSITORY.*GITHUB_EVENT_PATH.*GITHUB_REF/s,
|
|
153
|
+
);
|
|
154
|
+
await expect(reconcilePr({ env: "app", mode: "comment", body: "plan" })).rejects.toThrow(
|
|
155
|
+
/findingMode "issue" or "report"/,
|
|
156
|
+
);
|
|
157
|
+
} finally {
|
|
158
|
+
vi.unstubAllEnvs();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
});
|