@intentius/chant 0.79.0 → 0.81.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/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +30 -0
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts +5 -0
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +13 -0
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +13 -0
- package/dist/audit/terraform-state.d.ts.map +1 -1
- package/dist/cli/command-group.d.ts +6 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +44 -3
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +8 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/import-agents.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/update.d.ts +6 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/vendor.d.ts +5 -0
- package/dist/cli/commands/vendor.d.ts.map +1 -1
- package/dist/cli/conflict-check.d.ts +6 -0
- package/dist/cli/conflict-check.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/init.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +7 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/promote.d.ts +20 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -0
- package/dist/cli/handlers/run-generate.d.ts +39 -0
- package/dist/cli/handlers/run-generate.d.ts.map +1 -0
- package/dist/cli/handlers/run.d.ts +4 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/vendor.d.ts +8 -3
- package/dist/cli/handlers/vendor.d.ts.map +1 -1
- package/dist/cli/main.d.ts +12 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts +14 -0
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +46 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts +10 -0
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +3 -1
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/discover.d.ts.map +1 -1
- package/dist/components/driver.d.ts +16 -2
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/gate-plan.d.ts +40 -0
- package/dist/components/gate-plan.d.ts.map +1 -0
- package/dist/components/pilots/alb-ecs.pilot.d.ts +7 -1
- package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -1
- package/dist/components/presets/ecs-fargate.d.ts +10 -4
- package/dist/components/presets/ecs-fargate.d.ts.map +1 -1
- package/dist/components/promote.d.ts +264 -0
- package/dist/components/promote.d.ts.map +1 -0
- package/dist/components/sandbox/driver.d.ts.map +1 -1
- package/dist/components/sandbox/run.d.ts.map +1 -1
- package/dist/components/verbs/build-archive.d.ts +22 -11
- package/dist/components/verbs/build-archive.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/config-static.d.ts +59 -0
- package/dist/config-static.d.ts.map +1 -0
- package/dist/config.d.ts +104 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/content-digest.d.ts +17 -0
- package/dist/content-digest.d.ts.map +1 -0
- package/dist/discovery/files.d.ts +48 -4
- package/dist/discovery/files.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +32 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/sandbox/driver.d.ts +20 -0
- package/dist/discovery/sandbox/driver.d.ts.map +1 -1
- package/dist/discovery/sandbox/fork.d.ts +20 -0
- package/dist/discovery/sandbox/fork.d.ts.map +1 -1
- package/dist/discovery/walk.d.ts +119 -0
- package/dist/discovery/walk.d.ts.map +1 -0
- package/dist/graph-ir.d.ts +17 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/join-key.d.ts +31 -0
- package/dist/join-key.d.ts.map +1 -0
- package/dist/lexicon-module.d.ts +110 -0
- package/dist/lexicon-module.d.ts.map +1 -0
- package/dist/lexicon.d.ts +82 -1
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/build-ledger-store.d.ts.map +1 -1
- package/dist/lifecycle/digest.d.ts +5 -1
- package/dist/lifecycle/digest.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +20 -5
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +64 -0
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/lease.d.ts +8 -1
- package/dist/lifecycle/lease.d.ts.map +1 -1
- package/dist/lifecycle/legacy-digest.d.ts +59 -0
- package/dist/lifecycle/legacy-digest.d.ts.map +1 -0
- package/dist/lifecycle/member-ledger.d.ts +67 -0
- package/dist/lifecycle/member-ledger.d.ts.map +1 -0
- package/dist/lifecycle/plan-digest.d.ts +4 -3
- package/dist/lifecycle/plan-digest.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +42 -1
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/status.d.ts +90 -5
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +2 -3
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +32 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/propose-upgrade.d.ts +75 -0
- package/dist/op/activities/propose-upgrade.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -1
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-name.d.ts +7 -0
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-summary.d.ts +16 -0
- package/dist/op/gate-summary.d.ts.map +1 -1
- package/dist/op/gate.d.ts +16 -2
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/project-root.d.ts +26 -0
- package/dist/project-root.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.d.ts +37 -0
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts +11 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -0
- package/dist/workspace/checks/generated.d.ts +156 -0
- package/dist/workspace/checks/generated.d.ts.map +1 -0
- package/dist/workspace/checks/ledgers.d.ts +78 -0
- package/dist/workspace/checks/ledgers.d.ts.map +1 -0
- package/dist/workspace/checks/links.d.ts +25 -0
- package/dist/workspace/checks/links.d.ts.map +1 -0
- package/dist/workspace/checks/pipelines.d.ts +76 -0
- package/dist/workspace/checks/pipelines.d.ts.map +1 -0
- package/dist/workspace/checks.d.ts +161 -0
- package/dist/workspace/checks.d.ts.map +1 -0
- package/dist/workspace/compose-graph.d.ts +115 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -0
- package/dist/workspace/compose-reports.d.ts +68 -0
- package/dist/workspace/compose-reports.d.ts.map +1 -0
- package/dist/workspace/declaration.d.ts +220 -0
- package/dist/workspace/declaration.d.ts.map +1 -0
- package/dist/workspace/declaration.schema.json +434 -0
- package/dist/workspace/generated-files.d.ts +65 -0
- package/dist/workspace/generated-files.d.ts.map +1 -0
- package/dist/workspace/glob.d.ts +13 -0
- package/dist/workspace/glob.d.ts.map +1 -0
- package/dist/workspace/graph-cli.d.ts +75 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -0
- package/dist/workspace/init.d.ts +96 -0
- package/dist/workspace/init.d.ts.map +1 -0
- package/dist/workspace/jsonc.d.ts +38 -0
- package/dist/workspace/jsonc.d.ts.map +1 -0
- package/dist/workspace/kinds.d.ts +192 -0
- package/dist/workspace/kinds.d.ts.map +1 -0
- package/dist/workspace/lineage-check.d.ts +84 -0
- package/dist/workspace/lineage-check.d.ts.map +1 -0
- package/dist/workspace/lineage-cli.d.ts +31 -0
- package/dist/workspace/lineage-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-init.d.ts +101 -0
- package/dist/workspace/lineage-init.d.ts.map +1 -0
- package/dist/workspace/lineage-lock.d.ts +258 -0
- package/dist/workspace/lineage-lock.d.ts.map +1 -0
- package/dist/workspace/lineage-merge.d.ts +14 -0
- package/dist/workspace/lineage-merge.d.ts.map +1 -0
- package/dist/workspace/lineage-migrations.d.ts +178 -0
- package/dist/workspace/lineage-migrations.d.ts.map +1 -0
- package/dist/workspace/lineage-update.d.ts +59 -0
- package/dist/workspace/lineage-update.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts +40 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade.d.ts +146 -0
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -0
- package/dist/workspace/lineage-vendor.d.ts +63 -0
- package/dist/workspace/lineage-vendor.d.ts.map +1 -0
- package/dist/workspace/lineage-version.d.ts +32 -0
- package/dist/workspace/lineage-version.d.ts.map +1 -0
- package/dist/workspace/links.d.ts +150 -0
- package/dist/workspace/links.d.ts.map +1 -0
- package/dist/workspace/ls.d.ts +99 -0
- package/dist/workspace/ls.d.ts.map +1 -0
- package/dist/workspace/member-commands.d.ts +139 -0
- package/dist/workspace/member-commands.d.ts.map +1 -0
- package/dist/workspace/member-handles.d.ts +54 -0
- package/dist/workspace/member-handles.d.ts.map +1 -0
- package/dist/workspace/member-pipeline.d.ts +143 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -0
- package/dist/workspace/member-run.d.ts +67 -0
- package/dist/workspace/member-run.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +65 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -0
- package/dist/workspace/record-source.d.ts +22 -0
- package/dist/workspace/record-source.d.ts.map +1 -0
- package/dist/workspace/records-cli.d.ts +81 -0
- package/dist/workspace/records-cli.d.ts.map +1 -0
- package/dist/workspace/records.d.ts +118 -0
- package/dist/workspace/records.d.ts.map +1 -0
- package/dist/workspace/root-refusal.d.ts +53 -0
- package/dist/workspace/root-refusal.d.ts.map +1 -0
- package/dist/workspace/status.d.ts +150 -0
- package/dist/workspace/status.d.ts.map +1 -0
- package/dist/workspace/template-manifest.d.ts +90 -0
- package/dist/workspace/template-manifest.d.ts.map +1 -0
- package/dist/workspace/tree.d.ts +36 -0
- package/dist/workspace/tree.d.ts.map +1 -0
- package/dist/workspace/trust/attestor.d.ts +67 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -0
- package/dist/workspace/trust/policy.d.ts +111 -0
- package/dist/workspace/trust/policy.d.ts.map +1 -0
- package/dist/workspace/trust/provenance.d.ts +67 -0
- package/dist/workspace/trust/provenance.d.ts.map +1 -0
- package/dist/workspace/trust/ssh-commit.d.ts +43 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -0
- package/dist/workspace/trust/test-repo.d.ts +42 -0
- package/dist/workspace/trust/test-repo.d.ts.map +1 -0
- package/dist/workspace/trust/verify-cli.d.ts +9 -0
- package/dist/workspace/trust/verify-cli.d.ts.map +1 -0
- package/dist/workspace/trust/verify.d.ts +66 -0
- package/dist/workspace/trust/verify.d.ts.map +1 -0
- package/dist/workspace/which-chant.d.ts +58 -0
- package/dist/workspace/which-chant.d.ts.map +1 -0
- package/dist/workspace/workspace-kinds.schema.json +133 -0
- package/package.json +4 -3
- package/src/audit/core.ts +13 -2
- package/src/audit/discover.ts +102 -36
- package/src/audit/report-html.ts +3 -1
- package/src/audit/report-model.ts +14 -1
- package/src/audit/terraform-state.test.ts +81 -1
- package/src/audit/terraform-state.ts +25 -0
- package/src/cli/command-group.ts +7 -0
- package/src/cli/commands/audit-truncation.test.ts +167 -0
- package/src/cli/commands/audit.ts +132 -29
- package/src/cli/commands/check-lexicon-examples.ts +2 -1
- package/src/cli/commands/check-lexicon.ts +24 -0
- package/src/cli/commands/doctor.test.ts +30 -0
- package/src/cli/commands/doctor.ts +21 -0
- package/src/cli/commands/import-agents.ts +15 -4
- package/src/cli/commands/init.ts +76 -25
- package/src/cli/commands/lint.test.ts +52 -1
- package/src/cli/commands/lint.ts +41 -71
- package/src/cli/commands/onboard.ts +15 -0
- package/src/cli/commands/update.ts +18 -5
- package/src/cli/commands/vendor.ts +11 -6
- package/src/cli/conflict-check.test.ts +18 -1
- package/src/cli/conflict-check.ts +13 -0
- package/src/cli/discovery-skip.test.ts +168 -0
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +38 -0
- package/src/cli/handlers/build-member.test.ts +127 -0
- package/src/cli/handlers/build.test.ts +11 -0
- package/src/cli/handlers/build.ts +35 -1
- package/src/cli/handlers/components.test.ts +127 -0
- package/src/cli/handlers/components.ts +70 -8
- package/src/cli/handlers/dev.ts +6 -1
- package/src/cli/handlers/fan-out.ts +9 -2
- package/src/cli/handlers/graph.test.ts +20 -0
- package/src/cli/handlers/graph.ts +4 -2
- package/src/cli/handlers/init.ts +54 -1
- package/src/cli/handlers/lifecycle.ts +2 -1
- package/src/cli/handlers/misc.ts +16 -0
- package/src/cli/handlers/operator.test.ts +68 -0
- package/src/cli/handlers/operator.ts +39 -10
- package/src/cli/handlers/promote.test.ts +377 -0
- package/src/cli/handlers/promote.ts +391 -0
- package/src/cli/handlers/run-generate-member.test.ts +83 -0
- package/src/cli/handlers/run-generate.test.ts +158 -0
- package/src/cli/handlers/run-generate.ts +168 -0
- package/src/cli/handlers/run.test.ts +47 -0
- package/src/cli/handlers/run.ts +41 -6
- package/src/cli/handlers/vendor.ts +147 -43
- package/src/cli/main.test.ts +137 -1
- package/src/cli/main.ts +290 -24
- package/src/cli/mcp/op-tools.ts +2 -1
- package/src/cli/path-lexicon-messages.test.ts +152 -0
- package/src/cli/plugins.ts +43 -5
- package/src/cli/registry.ts +46 -1
- package/src/cli/static-config-read.test.ts +208 -0
- package/src/codegen/fetch.test.ts +63 -1
- package/src/codegen/fetch.ts +30 -1
- package/src/components/__fixtures__/alb-ecs-service.json +0 -8
- package/src/components/capability-plugin-loader.ts +3 -1
- package/src/components/cli-support.ts +9 -3
- package/src/components/discover.test.ts +27 -57
- package/src/components/discover.ts +20 -51
- package/src/components/driver.test.ts +90 -12
- package/src/components/driver.ts +43 -5
- package/src/components/fan-out-support.ts +2 -1
- package/src/components/gate-plan.ts +53 -0
- package/src/components/pilots/README.md +1 -1
- package/src/components/pilots/alb-ecs.pilot.ts +7 -7
- package/src/components/presets/ecs-fargate.ts +10 -5
- package/src/components/promote.test.ts +645 -0
- package/src/components/promote.ts +641 -0
- package/src/components/sandbox/driver.ts +8 -8
- package/src/components/sandbox/run.ts +4 -1
- package/src/components/verbs/build-archive.ts +27 -21
- package/src/components/verbs/run-agent.ts +3 -5
- package/src/config-static.test.ts +136 -0
- package/src/config-static.ts +232 -0
- package/src/config.test.ts +40 -0
- package/src/config.ts +161 -11
- package/src/content-digest.test.ts +120 -0
- package/src/content-digest.ts +21 -0
- package/src/discovery/files.test.ts +127 -1
- package/src/discovery/files.ts +91 -59
- package/src/discovery/fold-import.ts +127 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/sandbox/driver.ts +73 -35
- package/src/discovery/sandbox/fork-diagnostic.test.ts +136 -13
- package/src/discovery/sandbox/fork.ts +83 -43
- package/src/discovery/sandbox/path-lexicon-multifile.test.ts +285 -0
- package/src/discovery/sandbox/path-lexicon-parity.test.ts +162 -0
- package/src/discovery/walk.test.ts +263 -0
- package/src/discovery/walk.ts +334 -0
- package/src/effect-receipt.ts +2 -2
- package/src/graph-ir.ts +18 -1
- package/src/join-key.test.ts +25 -0
- package/src/join-key.ts +39 -0
- package/src/lexicon-module.test.ts +203 -0
- package/src/lexicon-module.ts +215 -0
- package/src/lexicon.ts +105 -1
- package/src/lifecycle/build-ledger-store.ts +13 -2
- package/src/lifecycle/digest.ts +9 -5
- package/src/lifecycle/gate-ledger.test.ts +20 -0
- package/src/lifecycle/gate-ledger.ts +28 -6
- package/src/lifecycle/git.test.ts +13 -1
- package/src/lifecycle/git.ts +221 -176
- package/src/lifecycle/lease.ts +25 -10
- package/src/lifecycle/legacy-digest.test.ts +206 -0
- package/src/lifecycle/legacy-digest.ts +103 -0
- package/src/lifecycle/member-ledger.test.ts +319 -0
- package/src/lifecycle/member-ledger.ts +145 -0
- package/src/lifecycle/plan-digest.ts +7 -7
- package/src/lifecycle/release-ledger.test.ts +20 -0
- package/src/lifecycle/release-ledger.ts +53 -3
- package/src/lifecycle/status.test.ts +151 -1
- package/src/lifecycle/status.ts +228 -7
- package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +2 -3
- package/src/op/activities/activity-contracts.ts +37 -0
- package/src/op/activities/index.ts +10 -0
- package/src/op/activities/propose-upgrade.ts +206 -0
- package/src/op/activity-contract-registry.ts +18 -4
- package/src/op/activity-registry.ts +26 -0
- package/src/op/discover.test.ts +14 -4
- package/src/op/discover.ts +19 -23
- package/src/op/gate-approval.ts +3 -1
- package/src/op/gate-name.ts +8 -0
- package/src/op/gate-summary.test.ts +14 -0
- package/src/op/gate-summary.ts +25 -2
- package/src/op/gate.ts +36 -7
- package/src/op/generate-pipeline.test.ts +14 -5
- package/src/op/generate-pipeline.ts +2 -1
- package/src/op/runtimes/local.ts +2 -1
- package/src/project-root.test.ts +66 -1
- package/src/project-root.ts +48 -0
- package/src/workspace/__fixtures__/contract-repo.ts +100 -0
- package/src/workspace/__fixtures__/two-member-workspace.ts +35 -0
- package/src/workspace/behold-kinds.test.ts +162 -0
- package/src/workspace/check-contract.test.ts +127 -0
- package/src/workspace/check.schema.json +347 -0
- package/src/workspace/checks/generated.test.ts +287 -0
- package/src/workspace/checks/generated.ts +475 -0
- package/src/workspace/checks/ledgers.test.ts +139 -0
- package/src/workspace/checks/ledgers.ts +246 -0
- package/src/workspace/checks/links.ts +183 -0
- package/src/workspace/checks/pipelines.test.ts +133 -0
- package/src/workspace/checks/pipelines.ts +225 -0
- package/src/workspace/checks-repo.test.ts +71 -0
- package/src/workspace/checks.test.ts +450 -0
- package/src/workspace/checks.ts +461 -0
- package/src/workspace/compose-graph.test.ts +146 -0
- package/src/workspace/compose-graph.ts +228 -0
- package/src/workspace/compose-reports.test.ts +109 -0
- package/src/workspace/compose-reports.ts +186 -0
- package/src/workspace/declaration.schema.json +434 -0
- package/src/workspace/declaration.test.ts +347 -0
- package/src/workspace/declaration.ts +623 -0
- package/src/workspace/generated-files.test.ts +114 -0
- package/src/workspace/generated-files.ts +94 -0
- package/src/workspace/glob.ts +49 -0
- package/src/workspace/graph-cli.ts +220 -0
- package/src/workspace/graph-contract.test.ts +204 -0
- package/src/workspace/graph.schema.json +662 -0
- package/src/workspace/init.test.ts +223 -0
- package/src/workspace/init.ts +585 -0
- package/src/workspace/jsonc.test.ts +65 -0
- package/src/workspace/jsonc.ts +231 -0
- package/src/workspace/kinds.test.ts +288 -0
- package/src/workspace/kinds.ts +506 -0
- package/src/workspace/lineage-check.ts +264 -0
- package/src/workspace/lineage-cli.ts +114 -0
- package/src/workspace/lineage-init.test.ts +231 -0
- package/src/workspace/lineage-init.ts +365 -0
- package/src/workspace/lineage-lock.test.ts +224 -0
- package/src/workspace/lineage-lock.ts +357 -0
- package/src/workspace/lineage-merge.ts +44 -0
- package/src/workspace/lineage-migrations.test.ts +178 -0
- package/src/workspace/lineage-migrations.ts +343 -0
- package/src/workspace/lineage-update.ts +160 -0
- package/src/workspace/lineage-upgrade-cli.ts +151 -0
- package/src/workspace/lineage-upgrade.test.ts +474 -0
- package/src/workspace/lineage-upgrade.ts +624 -0
- package/src/workspace/lineage-vendor.test.ts +126 -0
- package/src/workspace/lineage-vendor.ts +215 -0
- package/src/workspace/lineage-version.ts +127 -0
- package/src/workspace/links.test.ts +344 -0
- package/src/workspace/links.ts +331 -0
- package/src/workspace/ls-contract.test.ts +200 -0
- package/src/workspace/ls.schema.json +190 -0
- package/src/workspace/ls.ts +254 -0
- package/src/workspace/member-commands.e2e.test.ts +244 -0
- package/src/workspace/member-commands.test.ts +217 -0
- package/src/workspace/member-commands.ts +540 -0
- package/src/workspace/member-handles.test.ts +76 -0
- package/src/workspace/member-handles.ts +228 -0
- package/src/workspace/member-pipeline.test.ts +135 -0
- package/src/workspace/member-pipeline.ts +317 -0
- package/src/workspace/member-run.ts +146 -0
- package/src/workspace/read-contract.test.ts +129 -0
- package/src/workspace/reason-codes.test.ts +113 -0
- package/src/workspace/reason-codes.ts +81 -0
- package/src/workspace/record-source.ts +117 -0
- package/src/workspace/records-cli.ts +198 -0
- package/src/workspace/records-contract.test.ts +93 -0
- package/src/workspace/records.schema.json +150 -0
- package/src/workspace/records.test.ts +243 -0
- package/src/workspace/records.ts +384 -0
- package/src/workspace/root-refusal.ts +97 -0
- package/src/workspace/status-contract.test.ts +308 -0
- package/src/workspace/status.schema.json +210 -0
- package/src/workspace/status.ts +412 -0
- package/src/workspace/template-manifest.test.ts +79 -0
- package/src/workspace/template-manifest.ts +221 -0
- package/src/workspace/tree.ts +132 -0
- package/src/workspace/trust/attestor.ts +93 -0
- package/src/workspace/trust/policy.ts +306 -0
- package/src/workspace/trust/provenance.ts +181 -0
- package/src/workspace/trust/ssh-commit.ts +138 -0
- package/src/workspace/trust/test-repo.ts +138 -0
- package/src/workspace/trust/trust.test.ts +397 -0
- package/src/workspace/trust/verify-cli.ts +56 -0
- package/src/workspace/trust/verify.ts +194 -0
- package/src/workspace/which-chant.test.ts +117 -0
- package/src/workspace/which-chant.ts +151 -0
- package/src/workspace/workspace-kinds.schema.json +133 -0
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import * as baseActivities from "./activities";
|
|
14
14
|
import { ACTIVITY_PROFILES, type ActivityProfile } from "./activity-profiles";
|
|
15
|
+
import { importLexiconModule } from "../lexicon-module";
|
|
15
16
|
|
|
16
17
|
export type { ActivityProfile } from "./activity-profiles";
|
|
17
18
|
|
|
@@ -52,6 +53,16 @@ export async function loadActivities(lexicons: string[] = []): Promise<Map<strin
|
|
|
52
53
|
|
|
53
54
|
for (const name of lexicons) {
|
|
54
55
|
try {
|
|
56
|
+
// chant #2520 — a lexicon declared by module path has no subpath to
|
|
57
|
+
// import; its plugin's `activities()` member stands in for it.
|
|
58
|
+
const local = await importLexiconModule(name);
|
|
59
|
+
if (local !== undefined) {
|
|
60
|
+
const plugin = Object.values(local).find((v) => isActivityContributor(v) && v.name === name) as
|
|
61
|
+
| ActivityContributor
|
|
62
|
+
| undefined;
|
|
63
|
+
if (plugin) collectActivities(await plugin.activities(), activities);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
55
66
|
const spec = `@intentius/chant-lexicon-${name}/op/activities`;
|
|
56
67
|
collectActivities((await import(spec)) as Record<string, unknown>, activities);
|
|
57
68
|
} catch {
|
|
@@ -62,6 +73,21 @@ export async function loadActivities(lexicons: string[] = []): Promise<Map<strin
|
|
|
62
73
|
return activities;
|
|
63
74
|
}
|
|
64
75
|
|
|
76
|
+
/** A plugin-shaped export with an `activities()` member (`LexiconPlugin.activities`). Structural, so this layer does not import `../lexicon.ts`. */
|
|
77
|
+
interface ActivityContributor {
|
|
78
|
+
name: string;
|
|
79
|
+
activities(): Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isActivityContributor(value: unknown): value is ActivityContributor {
|
|
83
|
+
return (
|
|
84
|
+
typeof value === "object" &&
|
|
85
|
+
value !== null &&
|
|
86
|
+
typeof (value as { name?: unknown }).name === "string" &&
|
|
87
|
+
typeof (value as { activities?: unknown }).activities === "function"
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
65
91
|
/**
|
|
66
92
|
* The built-in profile table. Kept async because it is awaited on the CLI's
|
|
67
93
|
* run path alongside {@link loadActivities}, and because a hosted runtime may
|
package/src/op/discover.test.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { describe, test, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
2
4
|
import { discoverOps } from "./discover";
|
|
3
5
|
|
|
6
|
+
/** The example project that holds `ops/alb-deploy.op.ts`. */
|
|
7
|
+
const ALB_INFRA = resolve(dirname(fileURLToPath(import.meta.url)), "../../../../examples/gitlab-aws-alb-infra");
|
|
8
|
+
|
|
4
9
|
// Mock getRuntime to return git root pointing at the repo root. Partial, via
|
|
5
10
|
// `importOriginal`: the modules a discovered Op file pulls in reach the rest of
|
|
6
11
|
// this module (`moduleDir`, for the lint presets), and a wholesale replacement
|
|
@@ -23,14 +28,19 @@ vi.mock("../runtime-adapter", async (importOriginal) => ({
|
|
|
23
28
|
}));
|
|
24
29
|
|
|
25
30
|
describe("discoverOps", () => {
|
|
26
|
-
test("discovers alb-deploy.op.ts from
|
|
27
|
-
const { ops, errors } = await discoverOps();
|
|
31
|
+
test("discovers alb-deploy.op.ts from its own project", async () => {
|
|
32
|
+
const { ops, errors } = await discoverOps({ cwd: ALB_INFRA });
|
|
28
33
|
expect(errors).toHaveLength(0);
|
|
29
34
|
expect(ops.has("alb-deploy")).toBe(true);
|
|
30
35
|
});
|
|
31
36
|
|
|
32
|
-
test("
|
|
37
|
+
test("from the git root, a child project's Ops are not discovered (#2527)", async () => {
|
|
33
38
|
const { ops } = await discoverOps();
|
|
39
|
+
expect(ops.has("alb-deploy")).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("discovered Op has correct config shape", async () => {
|
|
43
|
+
const { ops } = await discoverOps({ cwd: ALB_INFRA });
|
|
34
44
|
const op = ops.get("alb-deploy");
|
|
35
45
|
expect(op).toBeDefined();
|
|
36
46
|
expect(op!.config.name).toBe("alb-deploy");
|
|
@@ -40,7 +50,7 @@ describe("discoverOps", () => {
|
|
|
40
50
|
});
|
|
41
51
|
|
|
42
52
|
test("filePath points to the .op.ts source file", async () => {
|
|
43
|
-
const { ops } = await discoverOps();
|
|
53
|
+
const { ops } = await discoverOps({ cwd: ALB_INFRA });
|
|
44
54
|
const op = ops.get("alb-deploy");
|
|
45
55
|
expect(op!.filePath).toMatch(/alb-deploy\.op\.ts$/);
|
|
46
56
|
});
|
package/src/op/discover.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { getRuntime } from "../runtime-adapter";
|
|
2
|
-
import { readdir } from "node:fs/promises";
|
|
3
2
|
import { existsSync } from "node:fs";
|
|
4
3
|
import { dirname, join, resolve } from "node:path";
|
|
5
4
|
import type { OpConfig } from "./types";
|
|
5
|
+
import { walkDiscovery, workspaceMemberDirs } from "../discovery/walk";
|
|
6
|
+
import { hasDiscoveryMarkerSync } from "../discovery/files";
|
|
7
|
+
import { resolveDiscoveryGlobs } from "../config";
|
|
6
8
|
|
|
7
9
|
export interface DiscoveredOp {
|
|
8
10
|
config: OpConfig;
|
|
@@ -52,28 +54,22 @@ async function findDiscoveryRoot(cwd?: string): Promise<string> {
|
|
|
52
54
|
return gitRoot;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
!entry.name.endsWith(".spec.ts")
|
|
72
|
-
) {
|
|
73
|
-
files.push(fullPath);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return files;
|
|
57
|
+
/**
|
|
58
|
+
* Every `*.op.ts` file below the Op root, through the one discovery walk
|
|
59
|
+
* (`../discovery/walk.ts`, #2527). The walk down stops at child projects
|
|
60
|
+
* whatever the root is, so a configless layout whose Op root is the git root
|
|
61
|
+
* no longer collects its sibling projects' Ops. The project's
|
|
62
|
+
* `exclude`/`include` globs and the skip marker apply.
|
|
63
|
+
*/
|
|
64
|
+
async function collectOpFiles(root: string): Promise<string[]> {
|
|
65
|
+
return walkDiscovery({
|
|
66
|
+
walker: "ops",
|
|
67
|
+
root,
|
|
68
|
+
globs: await resolveDiscoveryGlobs(root),
|
|
69
|
+
excludeDirs: await workspaceMemberDirs(root),
|
|
70
|
+
accept: (name, full) =>
|
|
71
|
+
name.endsWith(".op.ts") && !name.endsWith(".test.ts") && !name.endsWith(".spec.ts") && !hasDiscoveryMarkerSync(full),
|
|
72
|
+
});
|
|
77
73
|
}
|
|
78
74
|
|
|
79
75
|
/** The `OpConfig` behind an exported value, or `undefined` when the value is not an Op. An Op entity carries its config on `.props` (`./resource.ts`); `name` and `phases` are what every consumer of a discovered Op reads. */
|
package/src/op/gate-approval.ts
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
import { createHash } from "node:crypto";
|
|
35
|
+
import { lexiconModulePath } from "../lexicon-module";
|
|
35
36
|
import { isStepOutputRef, type StepOutputRef } from "./step-output-ref";
|
|
36
37
|
|
|
37
38
|
/** What a policy's decision does to the gate. */
|
|
@@ -221,7 +222,8 @@ function isContextValue(value: unknown): boolean {
|
|
|
221
222
|
|
|
222
223
|
/** Import `lexicon`'s evaluator. Throws a message that names the package when it is not installed or exports none. */
|
|
223
224
|
export async function loadGatePolicyEvaluator(lexicon: string): Promise<GatePolicyEvaluator> {
|
|
224
|
-
|
|
225
|
+
// chant #2520 — a lexicon declared by module path exports its evaluator from that module.
|
|
226
|
+
const spec = lexiconModulePath(lexicon) ?? `@intentius/chant-lexicon-${lexicon}/gate-policy`;
|
|
225
227
|
let mod: Partial<GatePolicyEvaluator>;
|
|
226
228
|
try {
|
|
227
229
|
mod = (await import(spec)) as Partial<GatePolicyEvaluator>;
|
package/src/op/gate-name.ts
CHANGED
|
@@ -29,6 +29,14 @@ import type { OpConfig, PhaseDefinition, StepDefinition } from "./types";
|
|
|
29
29
|
*/
|
|
30
30
|
export const FAN_OUT_GATE_OP = "fan-out";
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* The op name a `chant workspace upgrade` gate is recorded under (#2550). The
|
|
34
|
+
* gate is the scope (`.` for a plain project), and it binds the digest of the
|
|
35
|
+
* upgrade's patch. Like a fan-out, the upgrade is a command with no `*.op.ts`
|
|
36
|
+
* behind it, so `chant approve` must not warn that it found none.
|
|
37
|
+
*/
|
|
38
|
+
export const WORKSPACE_UPGRADE_GATE_OP = "workspace-upgrade";
|
|
39
|
+
|
|
32
40
|
/** Either spelling of a gate step's name. `gate` since #2202; `signalName` through 0.59.0. */
|
|
33
41
|
export interface GateNamed {
|
|
34
42
|
gate?: string;
|
|
@@ -58,6 +58,20 @@ describe("gatedRunSummaryMarkdown (#2243)", () => {
|
|
|
58
58
|
expect(md).toContain("and not the next run");
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
// #2574 with #2538: a component gate in a workspace member names both its
|
|
62
|
+
// environment in the approve line and the member's gate ledger path.
|
|
63
|
+
test("a member's component gate names its environment and its member ledger path", () => {
|
|
64
|
+
const md = gatedRunSummaryMarkdown({
|
|
65
|
+
op: "search-service",
|
|
66
|
+
gate: "release",
|
|
67
|
+
expiresAt: "2026-09-08T00:00:00Z",
|
|
68
|
+
environment: "prod",
|
|
69
|
+
ledgerPrefix: "_members/services/search/",
|
|
70
|
+
});
|
|
71
|
+
expect(md).toContain("chant approve search-service release --env prod --approver <you>");
|
|
72
|
+
expect(md).toContain("`_members/services/search/_gates/search-service.jsonl`");
|
|
73
|
+
});
|
|
74
|
+
|
|
61
75
|
test("a gate that binds no plan says nothing about one", () => {
|
|
62
76
|
expect(gatedRunSummaryMarkdown(summary)).not.toContain("and not the next run");
|
|
63
77
|
});
|
package/src/op/gate-summary.ts
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
import { appendFileSync } from "node:fs";
|
|
22
22
|
import { gateLedgerPath } from "../lifecycle/gate-ledger";
|
|
23
|
+
import { resolveMemberLedger } from "../lifecycle/member-ledger";
|
|
23
24
|
import { approveCommand } from "./gate";
|
|
24
25
|
|
|
25
26
|
/** What a gated run knows about the gate it stopped on. */
|
|
@@ -50,6 +51,28 @@ export interface GatedRunSummary {
|
|
|
50
51
|
* to see that the digest has moved on.
|
|
51
52
|
*/
|
|
52
53
|
planDigest?: string;
|
|
54
|
+
/**
|
|
55
|
+
* The project's ledger prefix, `_members/<member>/` for a workspace member
|
|
56
|
+
* (#2538), so the ledger path shown is the one the fact was written to.
|
|
57
|
+
* Absent at level 0 and for the root member.
|
|
58
|
+
*/
|
|
59
|
+
ledgerPrefix?: string;
|
|
60
|
+
/** The environment a component gate's pending fact was recorded in (#2574). The `chant approve` line names it. */
|
|
61
|
+
environment?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* `{ ledgerPrefix }` for the project at `cwd` when it is a workspace member,
|
|
66
|
+
* else `{}`. A summary is a report, so a declaration that cannot be read
|
|
67
|
+
* leaves the path as it is rather than failing the report.
|
|
68
|
+
*/
|
|
69
|
+
export async function summaryLedgerPrefix(cwd: string = process.cwd()): Promise<{ ledgerPrefix?: string }> {
|
|
70
|
+
try {
|
|
71
|
+
const { prefix } = await resolveMemberLedger(cwd);
|
|
72
|
+
return prefix ? { ledgerPrefix: prefix } : {};
|
|
73
|
+
} catch {
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
53
76
|
}
|
|
54
77
|
|
|
55
78
|
/**
|
|
@@ -78,10 +101,10 @@ export function gatedRunSummaryMarkdown(summary: GatedRunSummary): string {
|
|
|
78
101
|
"Approve it, then re-run this workflow:",
|
|
79
102
|
"",
|
|
80
103
|
"```",
|
|
81
|
-
`${approveCommand(summary.op, summary.gate)} --approver <you>`,
|
|
104
|
+
`${approveCommand(summary.op, summary.gate, summary.environment)} --approver <you>`,
|
|
82
105
|
"```",
|
|
83
106
|
"",
|
|
84
|
-
`Ledger: \`${gateLedgerPath(summary.op)}\` on the \`chant/lifecycle\` branch.`,
|
|
107
|
+
`Ledger: \`${gateLedgerPath(summary.op, summary.ledgerPrefix)}\` on the \`chant/lifecycle\` branch.`,
|
|
85
108
|
);
|
|
86
109
|
if (summary.expiresAt) lines.push(`Expires: ${summary.expiresAt}`);
|
|
87
110
|
if (summary.url) lines.push(`Approve at: ${summary.url}`);
|
package/src/op/gate.ts
CHANGED
|
@@ -170,6 +170,14 @@ export interface GateCheckInput {
|
|
|
170
170
|
* the standing pending fact satisfies the gate whatever has changed since.
|
|
171
171
|
*/
|
|
172
172
|
planDigest?: string;
|
|
173
|
+
/**
|
|
174
|
+
* The environment this run deploys to (#2574). The component driver sets it,
|
|
175
|
+
* because a component's gate ledger is keyed by component and holds every
|
|
176
|
+
* environment's facts. Set, the gate reads only this environment's pending
|
|
177
|
+
* facts and resolutions (plus resolutions that record no environment, which
|
|
178
|
+
* come back as a mismatch), and the pending fact it records carries it.
|
|
179
|
+
*/
|
|
180
|
+
environment?: string;
|
|
173
181
|
/**
|
|
174
182
|
* The gate's quorum and policy (#2508), context already resolved. Absent,
|
|
175
183
|
* one approval passes the gate, which is the rule every gate had before.
|
|
@@ -194,6 +202,8 @@ export interface GateDigestMismatch {
|
|
|
194
202
|
resolvedBy: string;
|
|
195
203
|
/** When they recorded it. */
|
|
196
204
|
timestamp: string;
|
|
205
|
+
/** The environment this run deploys to, on a component gate (#2574). The approve command a refusal prints names it. */
|
|
206
|
+
environment?: string;
|
|
197
207
|
}
|
|
198
208
|
|
|
199
209
|
/**
|
|
@@ -210,15 +220,18 @@ export interface GateDigestMismatch {
|
|
|
210
220
|
export function describeGateMismatch(op: string, gate: string, mismatch: GateDigestMismatch): string {
|
|
211
221
|
const why =
|
|
212
222
|
mismatch.approved === undefined
|
|
213
|
-
?
|
|
214
|
-
"
|
|
223
|
+
? mismatch.environment !== undefined
|
|
224
|
+
? "That approval predates environment- and plan-bound component gates (#2574) and records neither, " +
|
|
225
|
+
"so it cannot answer for this one. Approving again binds it:"
|
|
226
|
+
: "That resolution predates plan-bound gates (#2300) and records no plan at all, so it cannot " +
|
|
227
|
+
"answer for this one. Approving again binds it:"
|
|
215
228
|
: "The configuration or the live system changed between that approval and this plan, so it needs " +
|
|
216
229
|
"a fresh one:";
|
|
217
230
|
return (
|
|
218
231
|
`Gate "${gate}" is approved, but not for this plan. ` +
|
|
219
232
|
`approved: ${describePlanDigest(mismatch.approved)} (by ${mismatch.resolvedBy} at ${mismatch.timestamp}); ` +
|
|
220
233
|
`planned: ${mismatch.planned}. ` +
|
|
221
|
-
`${why} ${approveCommand(op, gate)}`
|
|
234
|
+
`${why} ${approveCommand(op, gate, mismatch.environment)}`
|
|
222
235
|
);
|
|
223
236
|
}
|
|
224
237
|
|
|
@@ -374,7 +387,17 @@ const EPOCH = new Date(0).toISOString();
|
|
|
374
387
|
*/
|
|
375
388
|
export async function evaluateGate(port: GateLedgerPort, input: GateCheckInput): Promise<GateCheck> {
|
|
376
389
|
const now = input.now ?? new Date().toISOString();
|
|
377
|
-
const
|
|
390
|
+
const ledger = await port.read(input.op);
|
|
391
|
+
// #2574: a component's ledger holds every environment's facts. This run
|
|
392
|
+
// answers only to its own environment's pending facts and approvals. An
|
|
393
|
+
// approval that records no environment at all is kept, so it comes back as
|
|
394
|
+
// a mismatch with a message instead of silently not counting.
|
|
395
|
+
const env = input.environment;
|
|
396
|
+
const pending = env === undefined ? ledger.pending : ledger.pending.filter((p) => p.environment === env);
|
|
397
|
+
const resolutions =
|
|
398
|
+
env === undefined
|
|
399
|
+
? ledger.resolutions
|
|
400
|
+
: ledger.resolutions.filter((r) => r.environment === undefined || r.environment === env);
|
|
378
401
|
|
|
379
402
|
const standing = latestPendingGate(pending, input.gate);
|
|
380
403
|
const since = standing?.timestamp ?? EPOCH;
|
|
@@ -410,6 +433,7 @@ export async function evaluateGate(port: GateLedgerPort, input: GateCheckInput):
|
|
|
410
433
|
planned: input.planDigest!,
|
|
411
434
|
resolvedBy: mismatched.resolvedBy,
|
|
412
435
|
timestamp: mismatched.timestamp,
|
|
436
|
+
...(env !== undefined ? { environment: env } : {}),
|
|
413
437
|
}
|
|
414
438
|
: undefined;
|
|
415
439
|
const asMismatch = { ...(mismatch ? { mismatch } : {}), ...(quorum ? { quorum } : {}) };
|
|
@@ -441,6 +465,7 @@ export async function evaluateGate(port: GateLedgerPort, input: GateCheckInput):
|
|
|
441
465
|
...(input.runId ? { runId: input.runId } : {}),
|
|
442
466
|
...(url ? { url } : {}),
|
|
443
467
|
...(input.planDigest !== undefined ? { planDigest: input.planDigest } : {}),
|
|
468
|
+
...(env !== undefined ? { environment: env } : {}),
|
|
444
469
|
...(input.approval ? { approval: input.approval } : {}),
|
|
445
470
|
});
|
|
446
471
|
return {
|
|
@@ -453,7 +478,11 @@ export async function evaluateGate(port: GateLedgerPort, input: GateCheckInput):
|
|
|
453
478
|
};
|
|
454
479
|
}
|
|
455
480
|
|
|
456
|
-
/**
|
|
457
|
-
|
|
458
|
-
|
|
481
|
+
/**
|
|
482
|
+
* The line every renderer prints to say how a pending gate is cleared. A
|
|
483
|
+
* component gate names its environment (#2574), so the approval picks that
|
|
484
|
+
* environment's pending fact when others stand beside it.
|
|
485
|
+
*/
|
|
486
|
+
export function approveCommand(op: string, gate: string, environment?: string): string {
|
|
487
|
+
return environment === undefined ? `chant approve ${op} ${gate}` : `chant approve ${op} ${gate} --env ${environment}`;
|
|
459
488
|
}
|
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
* `../components/cli-support.ts`'s `generateComponentsPipeline`. Mirrors that
|
|
4
4
|
* module's own test style (`cli-support.test.ts`): a minimal mocked lexicon
|
|
5
5
|
* plugin satisfying `isLexiconPlugin`, real `discoverOps()` resolution
|
|
6
|
-
* against this repo's actual `*.op.ts` fixtures
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* against this repo's actual `*.op.ts` fixtures
|
|
7
|
+
* (`examples/gitlab-aws-alb-infra/ops/alb-deploy.op.ts`, see
|
|
8
|
+
* `./discover.test.ts`) so Op-name validation exercises the real discovery
|
|
9
|
+
* path rather than a stub. Discovery starts in that example: since #2527 the
|
|
10
|
+
* walk from the git root stops at child projects, so the repo root finds none.
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
import { describe, test, expect, vi } from "vitest";
|
|
14
|
+
import { dirname, resolve } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
12
16
|
import { generateOpsPipeline, withOpSchedules } from "./generate-pipeline";
|
|
13
17
|
import type { DiscoveredOp } from "./discover";
|
|
14
18
|
import type { OpConfig } from "./types";
|
|
@@ -34,6 +38,9 @@ vi.mock("@intentius/chant-lexicon-gitlab", () => ({
|
|
|
34
38
|
},
|
|
35
39
|
}));
|
|
36
40
|
|
|
41
|
+
/** The example project that holds `alb-deploy.op.ts`. */
|
|
42
|
+
const ALB_INFRA = resolve(dirname(fileURLToPath(import.meta.url)), "../../../../examples/gitlab-aws-alb-infra");
|
|
43
|
+
|
|
37
44
|
describe("generateOpsPipeline", () => {
|
|
38
45
|
test("errors when the target lexicon has no generateOpPipeline", async () => {
|
|
39
46
|
const result = await generateOpsPipeline([{ name: "alb-deploy", schedule: "0 6 * * *" }], "aws");
|
|
@@ -45,6 +52,8 @@ describe("generateOpsPipeline", () => {
|
|
|
45
52
|
const result = await generateOpsPipeline(
|
|
46
53
|
[{ name: "definitely-not-a-real-op", schedule: "0 6 * * *" }],
|
|
47
54
|
"gitlab",
|
|
55
|
+
undefined,
|
|
56
|
+
ALB_INFRA,
|
|
48
57
|
);
|
|
49
58
|
expect(result.success).toBe(false);
|
|
50
59
|
expect(result.error).toMatch(/Unknown Op\(s\): definitely-not-a-real-op/);
|
|
@@ -52,7 +61,7 @@ describe("generateOpsPipeline", () => {
|
|
|
52
61
|
|
|
53
62
|
test("validates every named Op and delegates to the lexicon plugin", async () => {
|
|
54
63
|
const specs: ScheduledOpSpec[] = [{ name: "alb-deploy", schedule: "0 6 * * *", findingMode: "issue" }];
|
|
55
|
-
const result = await generateOpsPipeline(specs, "gitlab");
|
|
64
|
+
const result = await generateOpsPipeline(specs, "gitlab", undefined, ALB_INFRA);
|
|
56
65
|
expect(result.success).toBe(true);
|
|
57
66
|
expect(result.files).toEqual([{ name: "alb-deploy.yml", yaml: "# alb-deploy @ 0 6 * * *" }]);
|
|
58
67
|
expect(result.jobs).toEqual([
|
|
@@ -66,7 +75,7 @@ describe("generateOpsPipeline", () => {
|
|
|
66
75
|
// `PipelineAuditOp`, `ReconcileOp`) only ever sets `schedule`.
|
|
67
76
|
test("a legacy `{ schedule }` spec with no `trigger` round-trips unchanged", async () => {
|
|
68
77
|
const specs: ScheduledOpSpec[] = [{ name: "alb-deploy", schedule: "0 6 * * *" }];
|
|
69
|
-
const result = await generateOpsPipeline(specs, "gitlab");
|
|
78
|
+
const result = await generateOpsPipeline(specs, "gitlab", undefined, ALB_INFRA);
|
|
70
79
|
expect(result.success).toBe(true);
|
|
71
80
|
expect(result.files).toEqual([{ name: "alb-deploy.yml", yaml: "# alb-deploy @ 0 6 * * *" }]);
|
|
72
81
|
expect(result.jobs).toEqual([
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { discoverOps, type DiscoveredOp } from "./discover";
|
|
20
|
+
import { lexiconModulePath } from "../lexicon-module";
|
|
20
21
|
import {
|
|
21
22
|
isLexiconPlugin,
|
|
22
23
|
type LexiconPlugin,
|
|
@@ -36,7 +37,7 @@ import {
|
|
|
36
37
|
async function loadLexiconPlugin(name: string): Promise<LexiconPlugin | null> {
|
|
37
38
|
let mod: Record<string, unknown>;
|
|
38
39
|
try {
|
|
39
|
-
mod = (await import(`@intentius/chant-lexicon-${name}`)) as Record<string, unknown>;
|
|
40
|
+
mod = (await import(lexiconModulePath(name) ?? `@intentius/chant-lexicon-${name}`)) as Record<string, unknown>;
|
|
40
41
|
} catch {
|
|
41
42
|
return null;
|
|
42
43
|
}
|
package/src/op/runtimes/local.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* answers.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import { lexiconNames } from "../../lexicon-module";
|
|
21
22
|
import { loadChantConfig } from "../../config";
|
|
22
23
|
import { loadActivities, loadProfiles } from "../activity-registry";
|
|
23
24
|
import { runOpLocally, OpRunFailure, type OpRunResult } from "../local-executor";
|
|
@@ -129,7 +130,7 @@ export function createLocalOpRuntime(opts: { projectPath?: string } = {}): OpRun
|
|
|
129
130
|
// unreadable config just yields the base activities.
|
|
130
131
|
let lexicons: string[] = [];
|
|
131
132
|
try {
|
|
132
|
-
lexicons = (await loadChantConfig(projectPath)).config.lexicons ?? [];
|
|
133
|
+
lexicons = lexiconNames((await loadChantConfig(projectPath)).config.lexicons ?? []);
|
|
133
134
|
} catch {
|
|
134
135
|
// No/invalid chant.config — base activities only.
|
|
135
136
|
}
|
package/src/project-root.test.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
|
2
2
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { tmpdir } from "os";
|
|
5
|
-
import { findProjectConfig, findProjectRoot } from "./project-root";
|
|
5
|
+
import { findProjectConfig, findProjectRoot, findWorkspaceRoot } from "./project-root";
|
|
6
6
|
|
|
7
7
|
// Every test builds its own isolated directory tree under a fresh tmpdir —
|
|
8
8
|
// never reused across tests, and never anywhere near the real repo's own
|
|
@@ -103,3 +103,68 @@ describe("findProjectConfig / findProjectRoot (chant #1117)", () => {
|
|
|
103
103
|
expect(result.configPath).toBe(join(projectRoot, "chant.config.ts"));
|
|
104
104
|
});
|
|
105
105
|
});
|
|
106
|
+
|
|
107
|
+
describe("findWorkspaceRoot (chant #2534)", () => {
|
|
108
|
+
const declare = (dir: string, name = "chant.workspace.json") => {
|
|
109
|
+
mkdirSync(dir, { recursive: true });
|
|
110
|
+
writeFileSync(join(dir, name), "{}");
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
test("walks up to the nearest declaration, and never looks at chant.config", () => {
|
|
114
|
+
mkdirSync(join(root, ".git"));
|
|
115
|
+
declare(root);
|
|
116
|
+
const deep = join(root, "lexicons", "aws", "examples", "one");
|
|
117
|
+
mkdirSync(deep, { recursive: true });
|
|
118
|
+
writeFileSync(join(root, "lexicons", "aws", "package.json"), "{}");
|
|
119
|
+
writeFileSync(join(deep, "chant.config.ts"), "export default {};");
|
|
120
|
+
expect(findWorkspaceRoot(deep)).toEqual({ dir: root, file: join(root, "chant.workspace.json") });
|
|
121
|
+
// findProjectConfig is unchanged: it still stops at the project's own config.
|
|
122
|
+
expect(findProjectConfig(deep).dir).toBe(deep);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("the nearest declaration wins, so a nested workspace finds itself", () => {
|
|
126
|
+
mkdirSync(join(root, ".git"));
|
|
127
|
+
declare(root);
|
|
128
|
+
const inner = join(root, "vendor", "kit");
|
|
129
|
+
declare(inner, "chant.workspace.jsonc");
|
|
130
|
+
mkdirSync(join(inner, "src"));
|
|
131
|
+
expect(findWorkspaceRoot(join(inner, "src"))).toEqual({ dir: inner, file: join(inner, "chant.workspace.jsonc") });
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("stops at the git root after checking it", () => {
|
|
135
|
+
declare(root);
|
|
136
|
+
const repo = join(root, "repo");
|
|
137
|
+
mkdirSync(join(repo, ".git"), { recursive: true });
|
|
138
|
+
mkdirSync(join(repo, "src"));
|
|
139
|
+
expect(findWorkspaceRoot(join(repo, "src"))).toBeUndefined();
|
|
140
|
+
declare(repo);
|
|
141
|
+
expect(findWorkspaceRoot(join(repo, "src"))?.dir).toBe(repo);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("a .git file (a worktree or submodule) is a git root too", () => {
|
|
145
|
+
declare(root);
|
|
146
|
+
const repo = join(root, "wt");
|
|
147
|
+
mkdirSync(repo);
|
|
148
|
+
writeFileSync(join(repo, ".git"), "gitdir: /elsewhere\n");
|
|
149
|
+
expect(findWorkspaceRoot(repo)).toBeUndefined();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("outside a git repository only the start directory is checked", () => {
|
|
153
|
+
declare(root);
|
|
154
|
+
const sub = join(root, "sub");
|
|
155
|
+
mkdirSync(sub);
|
|
156
|
+
expect(findWorkspaceRoot(sub)).toBeUndefined();
|
|
157
|
+
expect(findWorkspaceRoot(root)?.dir).toBe(root);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("both names present is reported, not thrown", () => {
|
|
161
|
+
mkdirSync(join(root, ".git"));
|
|
162
|
+
declare(root);
|
|
163
|
+
declare(root, "chant.workspace.jsonc");
|
|
164
|
+
expect(findWorkspaceRoot(root)).toEqual({
|
|
165
|
+
dir: root,
|
|
166
|
+
file: join(root, "chant.workspace.json"),
|
|
167
|
+
conflicting: join(root, "chant.workspace.jsonc"),
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
package/src/project-root.ts
CHANGED
|
@@ -76,3 +76,51 @@ export function findProjectConfig(startDir: string): ProjectConfigSearch {
|
|
|
76
76
|
export function findProjectRoot(startDir: string): string {
|
|
77
77
|
return findProjectConfig(startDir).dir;
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
/** The two names a workspace declaration may have (#2524 D1). */
|
|
81
|
+
export const WORKSPACE_DECLARATION_FILES = ["chant.workspace.json", "chant.workspace.jsonc"] as const;
|
|
82
|
+
|
|
83
|
+
/** Where {@link findWorkspaceRoot} found a declaration. */
|
|
84
|
+
export interface WorkspaceRootSearch {
|
|
85
|
+
/** The directory holding the declaration. */
|
|
86
|
+
dir: string;
|
|
87
|
+
/** Absolute path of the declaration file. When both names are present this is the `.json` one. */
|
|
88
|
+
file: string;
|
|
89
|
+
/**
|
|
90
|
+
* Absolute path of the `.jsonc` file when `.json` is also there. Having both
|
|
91
|
+
* is an error, which the reader reports; the walk itself never throws.
|
|
92
|
+
*/
|
|
93
|
+
conflicting?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Walk up from `startDir` (inclusive) to the nearest `chant.workspace.json` or
|
|
98
|
+
* `chant.workspace.jsonc` (#2524 D1). The walk stops at the git root, the first
|
|
99
|
+
* directory holding `.git`, after checking it. Outside a git repository only
|
|
100
|
+
* `startDir` itself is checked, so a stray declaration in an unrelated
|
|
101
|
+
* ancestor is never picked up.
|
|
102
|
+
*
|
|
103
|
+
* It only tests whether files exist and loads nothing else, so a level-0
|
|
104
|
+
* command can afford to call it. {@link findProjectConfig} is unchanged and
|
|
105
|
+
* never looks at a declaration.
|
|
106
|
+
*/
|
|
107
|
+
export function findWorkspaceRoot(startDir: string): WorkspaceRootSearch | undefined {
|
|
108
|
+
const start = resolve(startDir);
|
|
109
|
+
let top: string | undefined;
|
|
110
|
+
for (let dir = start; ; dir = dirname(dir)) {
|
|
111
|
+
if (existsSync(join(dir, ".git"))) {
|
|
112
|
+
top = dir;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
if (dirname(dir) === dir) break;
|
|
116
|
+
}
|
|
117
|
+
for (let dir = start; ; dir = dirname(dir)) {
|
|
118
|
+
const [json, jsonc] = WORKSPACE_DECLARATION_FILES.map((name) => join(dir, name));
|
|
119
|
+
const hasJson = existsSync(json);
|
|
120
|
+
const hasJsonc = existsSync(jsonc);
|
|
121
|
+
if (hasJson && hasJsonc) return { dir, file: json, conflicting: jsonc };
|
|
122
|
+
if (hasJson) return { dir, file: json };
|
|
123
|
+
if (hasJsonc) return { dir, file: jsonc };
|
|
124
|
+
if (top === undefined || dir === top || dirname(dir) === dir) return undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|