@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
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant run --generate <provider>` (#2533): the CLI for `generateOpsPipeline`
|
|
3
|
+
* (../../op/generate-pipeline.ts), the Op counterpart to `chant build
|
|
4
|
+
* --components --generate <lexicon>`.
|
|
5
|
+
*
|
|
6
|
+
* The generator is a library function with no caller of its own. A pipeline
|
|
7
|
+
* committed to a repository needs a named command that regenerates it, so a
|
|
8
|
+
* drift check (#2524 D14) can rerun that command and compare. This handler is
|
|
9
|
+
* that command and adds nothing to the generator's output beyond one header
|
|
10
|
+
* line: every file it writes is the generator's own `yaml`, byte for byte,
|
|
11
|
+
* under a first line carrying {@link GENERATED_MARKER} and the command that
|
|
12
|
+
* produced it.
|
|
13
|
+
*
|
|
14
|
+
* Where the specs come from:
|
|
15
|
+
*
|
|
16
|
+
* - by default, every discovered Op that declares its own `schedule`, as a
|
|
17
|
+
* bare `{ name }` spec. `generateOpsPipeline` fills the cron in from the Op
|
|
18
|
+
* (`withOpSchedules`), so the result is exactly what a direct call with
|
|
19
|
+
* those names returns;
|
|
20
|
+
* - with `--spec <file>`, a JSON file holding either a `ScheduledOpSpec[]`
|
|
21
|
+
* or `{ ops, options }`, for Ops triggered by a pull request or a push, a
|
|
22
|
+
* finding mode, setup steps, or generator options. The file is passed to
|
|
23
|
+
* the generator unchanged.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
27
|
+
import { dirname, join, resolve } from "node:path";
|
|
28
|
+
import { discoverOps } from "../../op/discover";
|
|
29
|
+
import { generateOpsPipeline } from "../../op/generate-pipeline";
|
|
30
|
+
import { GENERATED_MARKER } from "../../discovery/files";
|
|
31
|
+
import { findWorkspaceRoot } from "../../project-root";
|
|
32
|
+
import type { ComponentPipelineOptions, ScheduledOpSpec } from "../../lexicon";
|
|
33
|
+
import { formatBold, formatError, formatSuccess } from "../format";
|
|
34
|
+
import type { CommandContext } from "../registry";
|
|
35
|
+
|
|
36
|
+
/** Where each forge reads its pipeline files from, relative to the repository root. `--output` overrides it. */
|
|
37
|
+
export const DEFAULT_OP_PIPELINE_DIRS: Record<string, string> = {
|
|
38
|
+
github: ".github/workflows",
|
|
39
|
+
forgejo: ".forgejo/workflows",
|
|
40
|
+
gitlab: ".",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** The first line of every file this command writes. */
|
|
44
|
+
export function opPipelineHeader(provider: string, specFile?: string): string {
|
|
45
|
+
const command = `chant run --generate ${provider}${specFile ? ` --spec ${specFile}` : ""}`;
|
|
46
|
+
return `# ${GENERATED_MARKER}. Regenerate with: ${command}\n`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface SpecFile {
|
|
50
|
+
ops: ScheduledOpSpec[];
|
|
51
|
+
options?: ComponentPipelineOptions;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Read `--spec`: a `ScheduledOpSpec[]`, or `{ ops, options }`. Throws with the file named on anything else. */
|
|
55
|
+
export function readOpSpecFile(path: string): SpecFile {
|
|
56
|
+
let parsed: unknown;
|
|
57
|
+
try {
|
|
58
|
+
parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
59
|
+
} catch (err) {
|
|
60
|
+
throw new Error(`--spec ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
61
|
+
}
|
|
62
|
+
const shape = "an array of Op specs, or { \"ops\": [...], \"options\": {...} }";
|
|
63
|
+
const ops = Array.isArray(parsed) ? parsed : (parsed as { ops?: unknown } | null)?.ops;
|
|
64
|
+
if (!Array.isArray(ops)) throw new Error(`--spec ${path} must hold ${shape}`);
|
|
65
|
+
for (const [i, spec] of ops.entries()) {
|
|
66
|
+
if (typeof (spec as { name?: unknown })?.name !== "string") {
|
|
67
|
+
throw new Error(`--spec ${path}: entry ${i} has no "name". Each entry names the Op it generates a pipeline for.`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const options = Array.isArray(parsed) ? undefined : (parsed as { options?: ComponentPipelineOptions }).options;
|
|
71
|
+
return { ops: ops as ScheduledOpSpec[], ...(options ? { options } : {}) };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function runOpGenerate(ctx: CommandContext): Promise<number> {
|
|
75
|
+
const { args } = ctx;
|
|
76
|
+
const provider = args.generate as string;
|
|
77
|
+
const specPath = args.opsSpec;
|
|
78
|
+
|
|
79
|
+
let ops: ScheduledOpSpec[];
|
|
80
|
+
let options: ComponentPipelineOptions | undefined;
|
|
81
|
+
if (specPath) {
|
|
82
|
+
try {
|
|
83
|
+
({ ops, options } = readOpSpecFile(resolve(specPath)));
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error(formatError({ message: err instanceof Error ? err.message : String(err) }));
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
const discovered = await discoverOps();
|
|
90
|
+
if (discovered.errors.length > 0) {
|
|
91
|
+
for (const e of discovered.errors) console.error(formatError({ message: e }));
|
|
92
|
+
return 1;
|
|
93
|
+
}
|
|
94
|
+
ops = [...discovered.ops.entries()]
|
|
95
|
+
.filter(([, op]) => op.config.schedule)
|
|
96
|
+
.map(([name]) => name)
|
|
97
|
+
.sort()
|
|
98
|
+
.map((name) => ({ name }));
|
|
99
|
+
if (ops.length === 0) {
|
|
100
|
+
console.error(formatError({
|
|
101
|
+
message: "No Op declares a schedule, so there is nothing to generate",
|
|
102
|
+
hint: "Give an Op a `schedule`, or pass --spec <file.json> naming the Ops and their triggers.",
|
|
103
|
+
}));
|
|
104
|
+
return 1;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Inside a workspace member (#2542), the files are the member's: filtered
|
|
109
|
+
// to its paths, run in its directory, named after it at the repository
|
|
110
|
+
// root's forge directory, and recorded as its generated files. The
|
|
111
|
+
// workspace module loads only when a declaration was found.
|
|
112
|
+
const found = findWorkspaceRoot(process.cwd());
|
|
113
|
+
let plan: import("../../workspace/member-pipeline").MemberOpPlan | undefined;
|
|
114
|
+
let memberPipeline: typeof import("../../workspace/member-pipeline") | undefined;
|
|
115
|
+
if (found) {
|
|
116
|
+
memberPipeline = await import("../../workspace/member-pipeline");
|
|
117
|
+
try {
|
|
118
|
+
plan = memberPipeline.planMemberOpPipelines({ projectDir: process.cwd(), found, provider, output: args.output, specFile: specPath });
|
|
119
|
+
} catch (err) {
|
|
120
|
+
console.error(formatError({ message: memberPipeline.describeError(err) }));
|
|
121
|
+
return 1;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const result = await generateOpsPipeline(ops, provider, plan ? { ...options, member: plan.member } : options);
|
|
126
|
+
if (!result.success) {
|
|
127
|
+
console.error(formatError({ message: result.error ?? "Failed to generate Op pipelines" }));
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (plan && memberPipeline) {
|
|
132
|
+
const header = memberPipeline.generatedHeader(plan.command);
|
|
133
|
+
if (args.format === "json") {
|
|
134
|
+
const files = (result.files ?? []).map((f) => ({
|
|
135
|
+
name: f.name,
|
|
136
|
+
path: memberPipeline.repoRelative(plan.ctx, join(plan.outDir, f.name)),
|
|
137
|
+
content: header + f.yaml,
|
|
138
|
+
}));
|
|
139
|
+
console.log(JSON.stringify({ member: plan.member.name, files, jobs: result.jobs ?? [] }, null, 2));
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
for (const path of memberPipeline.writeMemberOpPipelines(plan, result.files ?? [])) {
|
|
143
|
+
console.error(formatSuccess(`wrote ${formatBold(path)} for member ${formatBold(plan.member.name)}`));
|
|
144
|
+
}
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const header = opPipelineHeader(provider, specPath);
|
|
149
|
+
const outDir = args.output ?? DEFAULT_OP_PIPELINE_DIRS[provider] ?? ".";
|
|
150
|
+
const files = (result.files ?? []).map((f) => ({
|
|
151
|
+
name: f.name,
|
|
152
|
+
path: join(outDir, f.name),
|
|
153
|
+
content: header + f.yaml,
|
|
154
|
+
}));
|
|
155
|
+
|
|
156
|
+
if (args.format === "json") {
|
|
157
|
+
console.log(JSON.stringify({ files, jobs: result.jobs ?? [] }, null, 2));
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
for (const file of files) {
|
|
162
|
+
const target = resolve(file.path);
|
|
163
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
164
|
+
writeFileSync(target, file.content);
|
|
165
|
+
console.error(formatSuccess(`wrote ${formatBold(file.path)}`));
|
|
166
|
+
}
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
@@ -1157,6 +1157,53 @@ describe("runOpComponents", () => {
|
|
|
1157
1157
|
vi.restoreAllMocks();
|
|
1158
1158
|
});
|
|
1159
1159
|
|
|
1160
|
+
test("--digest-file writes <component>=<digest> for each release the run recorded (#2602)", async () => {
|
|
1161
|
+
runComponentsMock.mockResolvedValue({
|
|
1162
|
+
success: true,
|
|
1163
|
+
selected: ["svc", "infra"],
|
|
1164
|
+
run: {
|
|
1165
|
+
order: ["svc", "infra"],
|
|
1166
|
+
waves: [["svc", "infra"]],
|
|
1167
|
+
results: [
|
|
1168
|
+
{ component: "svc", ok: true, status: "ok", records: [] },
|
|
1169
|
+
{ component: "infra", ok: true, status: "ok", records: [] },
|
|
1170
|
+
],
|
|
1171
|
+
ok: true,
|
|
1172
|
+
status: "ok",
|
|
1173
|
+
},
|
|
1174
|
+
});
|
|
1175
|
+
maybeRecordAutoReleaseMock.mockImplementation(async (info: { component: string }) =>
|
|
1176
|
+
info.component === "svc"
|
|
1177
|
+
? { recorded: true, commit: "a".repeat(40), record: { version: 1, component: "svc", env: "staging", digest: "sha256:abc", gitSha: "x", runId: "local-1", timestamp: "t", actor: "a" } }
|
|
1178
|
+
: { recorded: false, reason: "no-digest" });
|
|
1179
|
+
writeFileSyncMock.mockClear();
|
|
1180
|
+
vi.spyOn(process.stderr, "write").mockImplementation(() => true);
|
|
1181
|
+
|
|
1182
|
+
const exit = await runOpComponents({ args: makeArgs({ path: "all", env: "staging", digestFile: "out/svc.digest" }), plugins: [], serializers: [] });
|
|
1183
|
+
|
|
1184
|
+
expect(exit).toBe(0);
|
|
1185
|
+
const call = writeFileSyncMock.mock.calls.find(([path]) => String(path).endsWith("out/svc.digest"));
|
|
1186
|
+
expect(call?.[1]).toBe("svc=sha256:abc\n");
|
|
1187
|
+
vi.restoreAllMocks();
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
test("--digest-file is written empty when nothing was recorded (#2602)", async () => {
|
|
1191
|
+
runComponentsMock.mockResolvedValue({
|
|
1192
|
+
success: true,
|
|
1193
|
+
selected: ["svc"],
|
|
1194
|
+
run: { order: ["svc"], waves: [["svc"]], results: [{ component: "svc", ok: true, status: "ok", records: [] }], ok: true, status: "ok" },
|
|
1195
|
+
});
|
|
1196
|
+
maybeRecordAutoReleaseMock.mockResolvedValue({ recorded: false, reason: "opted-out" });
|
|
1197
|
+
writeFileSyncMock.mockClear();
|
|
1198
|
+
vi.spyOn(process.stderr, "write").mockImplementation(() => true);
|
|
1199
|
+
|
|
1200
|
+
await runOpComponents({ args: makeArgs({ path: "svc", digestFile: "svc.digest" }), plugins: [], serializers: [] });
|
|
1201
|
+
|
|
1202
|
+
const call = writeFileSyncMock.mock.calls.find(([path]) => String(path).endsWith("svc.digest"));
|
|
1203
|
+
expect(call?.[1]).toBe("");
|
|
1204
|
+
vi.restoreAllMocks();
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1160
1207
|
test("a release-write error is surfaced as a warning but does not change the exit code", async () => {
|
|
1161
1208
|
runComponentsMock.mockResolvedValue({
|
|
1162
1209
|
success: true,
|
package/src/cli/handlers/run.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { lexiconNames } from "../../lexicon-module";
|
|
1
2
|
import { resolve, dirname } from "node:path";
|
|
2
3
|
import { writeFileSync, mkdirSync, readFileSync } from "node:fs";
|
|
3
4
|
import { loadChantConfig, resolveAutoReleaseDisabled, type ChantConfig } from "../../config";
|
|
@@ -5,8 +6,8 @@ import { discoverOps } from "../../op/discover";
|
|
|
5
6
|
import type { OpConfig } from "../../op/types";
|
|
6
7
|
import { loadActivities, loadProfiles } from "../../op/activity-registry";
|
|
7
8
|
import { runOpLocally, findPolicyGateStep, OpRunFailure, type StepRecord } from "../../op/local-executor";
|
|
8
|
-
import { approveCommand } from "../../op/gate";
|
|
9
|
-
import { writeGatedRunSummary, type GatedRunSummary } from "../../op/gate-summary";
|
|
9
|
+
import { approveCommand, describeGateMismatch } from "../../op/gate";
|
|
10
|
+
import { summaryLedgerPrefix, writeGatedRunSummary, type GatedRunSummary } from "../../op/gate-summary";
|
|
10
11
|
import { createLocalOpRuntime } from "../../op/runtimes/local";
|
|
11
12
|
import type { OpRuntimeProvider, OpRunStatus } from "../../op/runtime";
|
|
12
13
|
import { renderHuman, renderJson } from "../../op/local-output";
|
|
@@ -20,6 +21,8 @@ import { ndjsonProgressSink } from "../../components/run-progress";
|
|
|
20
21
|
import { maybeRecordAutoRelease } from "../../components/auto-release";
|
|
21
22
|
import { maybePersistBuildManifest } from "../../components/manifest-persistence";
|
|
22
23
|
import type { DriverComponentResult } from "../../components/driver";
|
|
24
|
+
import type { ReleaseRecord } from "../../lifecycle/release-ledger";
|
|
25
|
+
import { runOpGenerate } from "./run-generate";
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
28
|
* The exit code a run that stopped at an unapproved gate uses (#2119).
|
|
@@ -124,7 +127,7 @@ async function resolveOpRuntime(ctx: CommandContext): Promise<OpRuntimeProvider
|
|
|
124
127
|
// long wait to be told a name is wrong.
|
|
125
128
|
let configured: string[] = [];
|
|
126
129
|
try {
|
|
127
|
-
configured = (await loadChantConfig(resolve("."))).config.lexicons ?? [];
|
|
130
|
+
configured = lexiconNames((await loadChantConfig(resolve("."))).config.lexicons ?? []);
|
|
128
131
|
} catch {
|
|
129
132
|
// No/unreadable chant.config.ts — the error below says so by listing nothing.
|
|
130
133
|
}
|
|
@@ -524,6 +527,16 @@ function refusesPolicyGateUnderSandbox(ctx: CommandContext, config: OpConfig, op
|
|
|
524
527
|
}
|
|
525
528
|
|
|
526
529
|
export async function runOp(ctx: CommandContext): Promise<number> {
|
|
530
|
+
if (ctx.args.generate) {
|
|
531
|
+
if (ctx.args.components) {
|
|
532
|
+
console.error(formatError({
|
|
533
|
+
message: "`chant run --components --generate` is not a command",
|
|
534
|
+
hint: "Generate a component pipeline with `chant build --components --generate <lexicon>`, or Op pipelines with `chant run --generate <provider>`.",
|
|
535
|
+
}));
|
|
536
|
+
return 1;
|
|
537
|
+
}
|
|
538
|
+
return runOpGenerate(ctx);
|
|
539
|
+
}
|
|
527
540
|
if (ctx.args.components && ctx.args.report) {
|
|
528
541
|
console.error(formatError({
|
|
529
542
|
message: "--report is not supported with --components",
|
|
@@ -576,13 +589,16 @@ export async function runOp(ctx: CommandContext): Promise<number> {
|
|
|
576
589
|
* ledger for work `chant run --components` records. Each result is filtered on
|
|
577
590
|
* its own `ok`, so a fan-out that partly succeeded records exactly the
|
|
578
591
|
* components that did.
|
|
592
|
+
*
|
|
593
|
+
* Returns the records it wrote, for `--digest-file` (#2602).
|
|
579
594
|
*/
|
|
580
595
|
export async function recordAutoReleasesForRun(
|
|
581
596
|
results: DriverComponentResult[],
|
|
582
597
|
env: string,
|
|
583
598
|
runId: string,
|
|
584
599
|
disabled: boolean,
|
|
585
|
-
): Promise<
|
|
600
|
+
): Promise<ReleaseRecord[]> {
|
|
601
|
+
const recorded: ReleaseRecord[] = [];
|
|
586
602
|
for (const componentResult of results) {
|
|
587
603
|
if (!componentResult.ok) continue;
|
|
588
604
|
const outcome = await maybeRecordAutoRelease(
|
|
@@ -602,6 +618,7 @@ export async function recordAutoReleasesForRun(
|
|
|
602
618
|
message: `release record for "${componentResult.component}"@${env} was not recorded: ${outcome.error}`,
|
|
603
619
|
}));
|
|
604
620
|
} else if (outcome.recorded) {
|
|
621
|
+
recorded.push(outcome.record);
|
|
605
622
|
console.error(formatInfo(
|
|
606
623
|
`Recorded release: ${formatBold(componentResult.component)}@${env} -> ${outcome.record.digest} (commit ${outcome.commit.slice(0, 7)})`,
|
|
607
624
|
));
|
|
@@ -621,6 +638,7 @@ export async function recordAutoReleasesForRun(
|
|
|
621
638
|
));
|
|
622
639
|
}
|
|
623
640
|
}
|
|
641
|
+
return recorded;
|
|
624
642
|
}
|
|
625
643
|
|
|
626
644
|
// ── chant run --components <name|all> ────────────────────────────────────────
|
|
@@ -748,7 +766,12 @@ export async function runOpComponents(ctx: CommandContext): Promise<number> {
|
|
|
748
766
|
console.error(formatWarning({
|
|
749
767
|
message: `component "${result.gated.component}" is gated on "${gate.gate}" — pending approval`,
|
|
750
768
|
}));
|
|
751
|
-
|
|
769
|
+
// #2574: an approval stands, but for another plan or for none. Say so,
|
|
770
|
+
// with the command that replaces it.
|
|
771
|
+
if (result.gated.mismatch) {
|
|
772
|
+
console.error(formatWarning({ message: describeGateMismatch(gate.op, gate.gate, result.gated.mismatch) }));
|
|
773
|
+
}
|
|
774
|
+
console.error(formatInfo(`approve : ${approveCommand(gate.op, gate.gate, gate.environment)}`));
|
|
752
775
|
if (gate.url) console.error(formatInfo(`approve at: ${gate.url}`));
|
|
753
776
|
console.error(formatInfo(`expires : ${gate.expiresAt}`));
|
|
754
777
|
// #2310: this run's own append reached only the local chant/lifecycle
|
|
@@ -770,6 +793,8 @@ export async function runOpComponents(ctx: CommandContext): Promise<number> {
|
|
|
770
793
|
...(gate.url ? { url: gate.url } : {}),
|
|
771
794
|
...(result.gated.pushed === false ? { pushed: false, pushWarning: result.gated.pushWarning } : {}),
|
|
772
795
|
...(gate.planDigest ? { planDigest: gate.planDigest } : {}),
|
|
796
|
+
...(await summaryLedgerPrefix()),
|
|
797
|
+
...(gate.environment ? { environment: gate.environment } : {}),
|
|
773
798
|
},
|
|
774
799
|
gatedExit,
|
|
775
800
|
);
|
|
@@ -778,7 +803,16 @@ export async function runOpComponents(ctx: CommandContext): Promise<number> {
|
|
|
778
803
|
|
|
779
804
|
if (result.success && result.run) {
|
|
780
805
|
const disabled = resolveAutoReleaseDisabled(config, ctx.args.noReleaseRecord);
|
|
781
|
-
await recordAutoReleasesForRun(result.run.results, env, `local-${Date.now()}`, disabled);
|
|
806
|
+
const recorded = await recordAutoReleasesForRun(result.run.results, env, `local-${Date.now()}`, disabled);
|
|
807
|
+
// `--digest-file` (#2602): the releases this run recorded, one
|
|
808
|
+
// `<component>=<digest>` line each, the form `chant components promote
|
|
809
|
+
// --digest` takes. A generated promote job reads it to promote exactly
|
|
810
|
+
// what this job built, not whatever is latest in the environment.
|
|
811
|
+
if (ctx.args.digestFile) {
|
|
812
|
+
const digestPath = resolve(ctx.args.digestFile);
|
|
813
|
+
mkdirSync(dirname(digestPath), { recursive: true });
|
|
814
|
+
writeFileSync(digestPath, recorded.map((r) => `${r.component}=${r.digest}\n`).join(""));
|
|
815
|
+
}
|
|
782
816
|
}
|
|
783
817
|
|
|
784
818
|
return result.success ? 0 : 1;
|
|
@@ -891,6 +925,7 @@ export async function runOpOnRuntime(ctx: CommandContext): Promise<number> {
|
|
|
891
925
|
? { pushed: false, pushWarning: status.result.gatePushWarning }
|
|
892
926
|
: {}),
|
|
893
927
|
...(pending?.planDigest ? { planDigest: pending.planDigest } : {}),
|
|
928
|
+
...(await summaryLedgerPrefix()),
|
|
894
929
|
},
|
|
895
930
|
gatedExit,
|
|
896
931
|
);
|
|
@@ -1,61 +1,165 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { vendorPull, vendorCheck, MANIFEST_FILE } from "../commands/vendor";
|
|
3
4
|
import { formatError, formatSuccess, formatWarning } from "../format";
|
|
4
5
|
import type { CommandContext } from "../registry";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* Where the lineage lock lives (`../../workspace/lineage-lock.ts` owns the
|
|
9
|
+
* constant). Repeated here so that finding out whether a lock exists loads no
|
|
10
|
+
* workspace module: a project with only `vendor.json` must see today's
|
|
11
|
+
* `chant vendor` exactly (#2525 rule 2).
|
|
12
|
+
*/
|
|
13
|
+
const LOCK_FILE = ".chant/workspace.lock.json";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `chant vendor [pull|check|migrate]` — pull pinned, checksummed patterns into
|
|
17
|
+
* the repo, check vendored targets against their pin, or move `vendor.json`
|
|
18
|
+
* into the lineage lock. Defaults to `pull`.
|
|
9
19
|
*
|
|
10
|
-
*
|
|
20
|
+
* `vendor.json` and the lock both live in the current directory. With no lock,
|
|
21
|
+
* `pull` and `check` read `vendor.json` exactly as before. With a lock, its
|
|
22
|
+
* vendor scopes are pulled file by file (local edits are kept, conflicts
|
|
23
|
+
* become manual steps), and any `vendor.json` entries not yet migrated are
|
|
24
|
+
* still pulled the old way (#2540).
|
|
11
25
|
*/
|
|
12
26
|
export async function runVendor(ctx: CommandContext): Promise<number> {
|
|
13
27
|
const { args } = ctx;
|
|
14
|
-
// `chant vendor` → pull; `chant vendor pull|check [name]`.
|
|
28
|
+
// `chant vendor` → pull; `chant vendor pull|check|migrate [name]`.
|
|
15
29
|
const sub = args.path === "." ? "pull" : args.path;
|
|
16
30
|
const manifestDir = resolve(".");
|
|
17
31
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
console.error(formatSuccess(`Vendored ${result.pulled.length} artifact(s)`));
|
|
27
|
-
return 0;
|
|
28
|
-
} catch (err) {
|
|
29
|
-
console.error(formatError({ message: err instanceof Error ? err.message : String(err) }));
|
|
30
|
-
return 1;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (sub === "check") {
|
|
35
|
-
try {
|
|
36
|
-
const result = vendorCheck(manifestDir);
|
|
37
|
-
console.log(result.output);
|
|
38
|
-
if (!result.drift) {
|
|
39
|
-
console.error(formatSuccess("All vendored artifacts match their pin"));
|
|
40
|
-
return 0;
|
|
41
|
-
}
|
|
42
|
-
// Drift is allowed (you may edit vendored files); fail only in CI so a
|
|
43
|
-
// pipeline catches unrecorded changes, warn locally.
|
|
44
|
-
if (process.env.CI) {
|
|
45
|
-
console.error(formatError({ message: "Vendored content drifted from the manifest pin" }));
|
|
46
|
-
return 1;
|
|
47
|
-
}
|
|
48
|
-
console.error(formatWarning({ message: "Vendored content drifted from the manifest pin (run `chant vendor pull` to re-pin)" }));
|
|
49
|
-
return 0;
|
|
50
|
-
} catch (err) {
|
|
51
|
-
console.error(formatError({ message: err instanceof Error ? err.message : String(err) }));
|
|
52
|
-
return 1;
|
|
53
|
-
}
|
|
32
|
+
try {
|
|
33
|
+
if (sub === "migrate") return await runMigrate(manifestDir);
|
|
34
|
+
const locked = await hasVendorScopes(manifestDir);
|
|
35
|
+
if (sub === "pull") return locked ? await runLockedPull(manifestDir, args.extraPositional) : await runPull(manifestDir, args.extraPositional);
|
|
36
|
+
if (sub === "check") return locked ? await runLockedCheck(manifestDir) : runCheck(manifestDir);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.error(formatError({ message: err instanceof Error ? err.message : String(err) }));
|
|
39
|
+
return 1;
|
|
54
40
|
}
|
|
55
41
|
|
|
56
42
|
console.error(formatError({
|
|
57
43
|
message: `Unknown vendor subcommand: ${sub}`,
|
|
58
|
-
hint: "Available: chant vendor pull [name], chant vendor check",
|
|
44
|
+
hint: "Available: chant vendor pull [name], chant vendor check, chant vendor migrate",
|
|
59
45
|
}));
|
|
60
46
|
return 1;
|
|
61
47
|
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Whether the lock holds any vendor scope. Without a lock file this loads
|
|
51
|
+
* nothing; a lock with no vendor scope (say, one `chant init --template`
|
|
52
|
+
* wrote) leaves `chant vendor` on `vendor.json` as before.
|
|
53
|
+
*/
|
|
54
|
+
async function hasVendorScopes(root: string): Promise<boolean> {
|
|
55
|
+
if (!existsSync(join(root, LOCK_FILE))) return false;
|
|
56
|
+
const { readLock } = await import("../../workspace/lineage-lock");
|
|
57
|
+
const lock = readLock(root);
|
|
58
|
+
return !!lock && Object.values(lock.scopes).some((l) => l.kind === "vendor");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── vendor.json only: unchanged ──────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
async function runPull(manifestDir: string, only?: string): Promise<number> {
|
|
64
|
+
const result = await vendorPull(manifestDir, only);
|
|
65
|
+
if (!result.success) {
|
|
66
|
+
console.error(formatError({ message: result.output }));
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
console.log(result.output);
|
|
70
|
+
console.error(formatSuccess(`Vendored ${result.pulled.length} artifact(s)`));
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function runCheck(manifestDir: string): number {
|
|
75
|
+
const result = vendorCheck(manifestDir);
|
|
76
|
+
console.log(result.output);
|
|
77
|
+
if (!result.drift) {
|
|
78
|
+
console.error(formatSuccess("All vendored artifacts match their pin"));
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
// Drift is allowed (you may edit vendored files); fail only in CI so a
|
|
82
|
+
// pipeline catches unrecorded changes, warn locally.
|
|
83
|
+
if (process.env.CI) {
|
|
84
|
+
console.error(formatError({ message: "Vendored content drifted from the manifest pin" }));
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
console.error(formatWarning({ message: "Vendored content drifted from the manifest pin (run `chant vendor pull` to re-pin)" }));
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ── With a lineage lock ──────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
const MIGRATE_HINT = `${MANIFEST_FILE} entries are not in ${LOCK_FILE} yet, and pull still overwrites their targets. Run \`chant vendor migrate\` to move them.`;
|
|
94
|
+
|
|
95
|
+
async function runLockedPull(root: string, only?: string): Promise<number> {
|
|
96
|
+
const lv = await import("../../workspace/lineage-vendor");
|
|
97
|
+
const hasManifest = lv.manifestPresent(root);
|
|
98
|
+
const legacy = hasManifest ? (await import("../commands/vendor")).loadManifest(root).manifest.vendored : [];
|
|
99
|
+
const legacyMatch = only ? legacy.some((e) => e.name === only) : legacy.length > 0;
|
|
100
|
+
|
|
101
|
+
const pulled = await lv.pullLockedScopes(root, only);
|
|
102
|
+
if (only && pulled.length === 0 && !legacyMatch) {
|
|
103
|
+
console.error(formatError({ message: `no vendored entry named "${only}"` }));
|
|
104
|
+
return 1;
|
|
105
|
+
}
|
|
106
|
+
const lines = pulled.map(lv.formatLockedPull);
|
|
107
|
+
let count = pulled.length;
|
|
108
|
+
if (legacyMatch) {
|
|
109
|
+
const result = await vendorPull(root, only);
|
|
110
|
+
lines.push(result.output);
|
|
111
|
+
count += result.pulled.length;
|
|
112
|
+
}
|
|
113
|
+
if (lines.length > 0) console.log(lines.join("\n"));
|
|
114
|
+
if (legacyMatch) console.error(formatWarning({ message: MIGRATE_HINT }));
|
|
115
|
+
|
|
116
|
+
const steps = pulled.reduce((n, p) => n + p.manualSteps.length, 0);
|
|
117
|
+
if (steps > 0) {
|
|
118
|
+
console.error(formatWarning({
|
|
119
|
+
message: `Vendored ${count} artifact(s); ${steps} file(s) changed both here and upstream were left as they are`,
|
|
120
|
+
}));
|
|
121
|
+
console.error(" Merge each by hand, then run `chant workspace lineage resolve <path>`.");
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
console.error(formatSuccess(`Vendored ${count} artifact(s)`));
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function runLockedCheck(root: string): Promise<number> {
|
|
129
|
+
const lv = await import("../../workspace/lineage-vendor");
|
|
130
|
+
const checks = lv.checkLockedScopes(root);
|
|
131
|
+
const lines = checks.map(lv.formatLockedCheck);
|
|
132
|
+
let drift = checks.some(lv.lockedCheckFails);
|
|
133
|
+
if (lv.manifestPresent(root)) {
|
|
134
|
+
const legacy = vendorCheck(root);
|
|
135
|
+
if (legacy.output) lines.push(legacy.output);
|
|
136
|
+
drift ||= legacy.drift;
|
|
137
|
+
console.error(formatWarning({ message: MIGRATE_HINT }));
|
|
138
|
+
}
|
|
139
|
+
if (lines.length > 0) console.log(lines.join("\n"));
|
|
140
|
+
if (!drift) {
|
|
141
|
+
console.error(formatSuccess("All vendored artifacts match their pin, or carry local edits"));
|
|
142
|
+
return 0;
|
|
143
|
+
}
|
|
144
|
+
const message = "Vendored content has open manual steps or missing targets";
|
|
145
|
+
if (process.env.CI) {
|
|
146
|
+
console.error(formatError({ message }));
|
|
147
|
+
return 1;
|
|
148
|
+
}
|
|
149
|
+
console.error(formatWarning({ message: `${message} (see \`chant workspace lineage\`)` }));
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function runMigrate(root: string): Promise<number> {
|
|
154
|
+
const { migrateVendorManifest } = await import("../../workspace/lineage-vendor");
|
|
155
|
+
const result = await migrateVendorManifest(root);
|
|
156
|
+
for (const m of result.migrated) {
|
|
157
|
+
console.log(` ${m.name} → ${m.target} (${m.pinned ? `${m.files} file(s)` : "unpinned"})`);
|
|
158
|
+
}
|
|
159
|
+
console.error(
|
|
160
|
+
formatSuccess(
|
|
161
|
+
`Moved ${result.migrated.length} vendored artifact(s) from ${MANIFEST_FILE} into ${LOCK_FILE}${result.lockCreated ? " (created)" : ""}; ${MANIFEST_FILE} removed`,
|
|
162
|
+
),
|
|
163
|
+
);
|
|
164
|
+
return 0;
|
|
165
|
+
}
|