@intentius/chant 0.80.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/discover.d.ts +8 -16
- 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 +4 -5
- package/dist/audit/terraform-state.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +25 -9
- package/dist/cli/commands/audit.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.map +1 -1
- package/dist/cli/commands/lint.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/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/operator.d.ts +7 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/promote.d.ts +7 -4
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/run-generate.d.ts.map +1 -1
- 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/plugins.d.ts +12 -7
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +32 -0
- 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/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/gate-plan.d.ts +40 -0
- package/dist/components/gate-plan.d.ts.map +1 -0
- package/dist/components/promote.d.ts +60 -7
- package/dist/components/promote.d.ts.map +1 -1
- 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 +32 -10
- 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 +10 -5
- package/dist/discovery/files.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +20 -4
- 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 +4 -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 +51 -6
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lexicon.d.ts +58 -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 +41 -8
- package/dist/lifecycle/legacy-digest.d.ts.map +1 -1
- 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 +18 -2
- 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/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/discover.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/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/records-cli.d.ts +28 -4
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records.d.ts.map +1 -1
- 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 +2 -1
- package/src/audit/discover.ts +66 -78
- package/src/audit/report-html.ts +3 -1
- package/src/audit/report-model.ts +14 -1
- package/src/audit/terraform-state.test.ts +4 -9
- package/src/audit/terraform-state.ts +4 -5
- package/src/cli/commands/audit-truncation.test.ts +167 -0
- package/src/cli/commands/audit.ts +89 -60
- package/src/cli/commands/check-lexicon.ts +24 -0
- package/src/cli/commands/doctor.test.ts +30 -0
- package/src/cli/commands/doctor.ts +18 -0
- package/src/cli/commands/import-agents.ts +6 -2
- package/src/cli/commands/init.ts +22 -3
- package/src/cli/commands/lint.test.ts +17 -23
- package/src/cli/commands/lint.ts +24 -84
- package/src/cli/commands/update.ts +13 -4
- package/src/cli/commands/vendor.ts +11 -6
- 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.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 +5 -3
- package/src/cli/handlers/fan-out.ts +9 -2
- package/src/cli/handlers/init.ts +54 -1
- package/src/cli/handlers/operator.test.ts +68 -0
- package/src/cli/handlers/operator.ts +37 -9
- package/src/cli/handlers/promote.test.ts +118 -2
- package/src/cli/handlers/promote.ts +82 -16
- package/src/cli/handlers/run-generate-member.test.ts +83 -0
- package/src/cli/handlers/run-generate.test.ts +6 -8
- package/src/cli/handlers/run-generate.ts +36 -1
- package/src/cli/handlers/run.test.ts +47 -0
- package/src/cli/handlers/run.ts +28 -5
- package/src/cli/handlers/vendor.ts +147 -43
- package/src/cli/main.test.ts +105 -1
- package/src/cli/main.ts +230 -17
- package/src/cli/plugins.ts +17 -17
- package/src/cli/registry.ts +32 -0
- 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/cli-support.ts +6 -1
- package/src/components/discover.test.ts +27 -57
- package/src/components/discover.ts +13 -68
- package/src/components/driver.test.ts +78 -1
- package/src/components/driver.ts +43 -7
- package/src/components/gate-plan.ts +53 -0
- package/src/components/promote.test.ts +276 -2
- package/src/components/promote.ts +119 -15
- 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 +2 -2
- package/src/config.ts +48 -15
- package/src/content-digest.test.ts +120 -0
- package/src/content-digest.ts +21 -0
- package/src/discovery/files.test.ts +10 -10
- package/src/discovery/files.ts +25 -81
- package/src/discovery/fold-import.ts +49 -5
- 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/walk.test.ts +263 -0
- package/src/discovery/walk.ts +334 -0
- package/src/effect-receipt.ts +2 -2
- package/src/graph-ir.ts +4 -1
- package/src/join-key.test.ts +25 -0
- package/src/join-key.ts +39 -0
- package/src/lexicon-module.ts +108 -10
- package/src/lexicon.ts +79 -1
- package/src/lifecycle/build-ledger-store.ts +11 -10
- 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 +115 -23
- package/src/lifecycle/legacy-digest.ts +60 -19
- 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 +23 -7
- package/src/lifecycle/status.test.ts +151 -1
- package/src/lifecycle/status.ts +228 -7
- 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/discover.test.ts +14 -4
- package/src/op/discover.ts +19 -27
- 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/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/records-cli.ts +86 -14
- package/src/workspace/records-contract.test.ts +5 -0
- package/src/workspace/records.schema.json +32 -2
- package/src/workspace/records.ts +3 -2
- 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
- package/dist/components/unbound-gate-approval.d.ts +0 -24
- package/dist/components/unbound-gate-approval.d.ts.map +0 -1
- package/dist/discovery/convergence.d.ts +0 -97
- package/dist/discovery/convergence.d.ts.map +0 -1
- package/src/cli/commands/audit-walk-warnings.test.ts +0 -156
- package/src/components/unbound-gate-approval.test.ts +0 -82
- package/src/components/unbound-gate-approval.ts +0 -51
- package/src/discovery/convergence.test.ts +0 -231
- package/src/discovery/convergence.ts +0 -399
- package/src/op/discover-convergence.test.ts +0 -70
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
* before anything after it runs;
|
|
18
18
|
* - gates, apply, verify and every other step run exactly as a normal deploy
|
|
19
19
|
* to the target environment would run them, so the target's gates apply.
|
|
20
|
+
* A gate approval is bound to the target environment, the transformed
|
|
21
|
+
* composition and the digest being deployed (#2574), so an approval for a
|
|
22
|
+
* plain deploy or for another release does not pass it.
|
|
20
23
|
*
|
|
21
24
|
* A composition the transform cannot pin to one recorded digest is refused
|
|
22
25
|
* before anything runs: one with no publish step (nothing would carry the
|
|
@@ -27,7 +30,9 @@
|
|
|
27
30
|
* A rollback (#2531) is the same run against one environment's own earlier
|
|
28
31
|
* release. There the publish step is replaced by the recorded digest rather
|
|
29
32
|
* than run, since the environment already received that artifact when the
|
|
30
|
-
* release was first deployed (see `withoutBuildSteps`'s `pinDigest`).
|
|
33
|
+
* release was first deployed (see `withoutBuildSteps`'s `pinDigest`). A
|
|
34
|
+
* redeploy (#2604) is a rollback to the release the ledger already records as
|
|
35
|
+
* current, for an environment a failed deploy left running something else.
|
|
31
36
|
*
|
|
32
37
|
* The driver stays capability-agnostic. The two kind lists below are data, in
|
|
33
38
|
* the same spirit as `DEPLOY_UNIT_RULES` (./deploy-units.ts).
|
|
@@ -114,12 +119,41 @@ export function selectRelease(
|
|
|
114
119
|
return { record: latestPerComponent(matching).get(component)! };
|
|
115
120
|
}
|
|
116
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Read `--digest <component>=<digest>` values (#2602) into a component-to-
|
|
124
|
+
* digest map. A value with no `=` is left to the single-component form
|
|
125
|
+
* (`--component <name> --digest <digest>`), so it is an error here. An empty
|
|
126
|
+
* digest is an error too: a generated promote job passes one when the job that
|
|
127
|
+
* deployed the component recorded no release, and promoting whatever is
|
|
128
|
+
* latest instead is the thing the pin is there to prevent.
|
|
129
|
+
*/
|
|
130
|
+
export function parseDigestPins(values: string[]): { pins: Record<string, string> } | { error: string } {
|
|
131
|
+
const pins: Record<string, string> = {};
|
|
132
|
+
for (const value of values) {
|
|
133
|
+
const at = value.indexOf("=");
|
|
134
|
+
if (at < 0) return { error: "--digest picks one release of one component, so it needs --component <name>, or the form <component>=<digest>" };
|
|
135
|
+
const component = value.slice(0, at);
|
|
136
|
+
const digest = value.slice(at + 1);
|
|
137
|
+
if (component === "") return { error: `--digest ${value} names no component` };
|
|
138
|
+
if (digest === "") {
|
|
139
|
+
return { error: `--digest ${value} names no digest; the run that deployed "${component}" recorded no release for it` };
|
|
140
|
+
}
|
|
141
|
+
if (component in pins && pins[component] !== digest) {
|
|
142
|
+
return { error: `--digest pins "${component}" twice, to ${pins[component]} and ${digest}` };
|
|
143
|
+
}
|
|
144
|
+
pins[component] = digest;
|
|
145
|
+
}
|
|
146
|
+
return { pins };
|
|
147
|
+
}
|
|
148
|
+
|
|
117
149
|
/**
|
|
118
150
|
* Decide which releases a promote deploys. With `component`, exactly that
|
|
119
|
-
* component's latest source release (or the one recording `digest`).
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
151
|
+
* component's latest source release (or the one recording `digest`). With
|
|
152
|
+
* `pins` (#2602), exactly the pinned components, each at the source release
|
|
153
|
+
* recording its pinned digest. Otherwise, the latest source release of every
|
|
154
|
+
* component this checkout declares; a recorded component the checkout no
|
|
155
|
+
* longer declares is reported and left alone, since there is no composition
|
|
156
|
+
* to deploy it with.
|
|
123
157
|
*/
|
|
124
158
|
export function planPromotion(input: {
|
|
125
159
|
from: string;
|
|
@@ -129,13 +163,29 @@ export function planPromotion(input: {
|
|
|
129
163
|
declared: string[];
|
|
130
164
|
component?: string;
|
|
131
165
|
digest?: string;
|
|
166
|
+
/** Component name to the digest to promote for it, from `--digest <component>=<digest>`. */
|
|
167
|
+
pins?: Record<string, string>;
|
|
132
168
|
}): PromotionPlan | { error: string } {
|
|
133
|
-
const { from, to, sourceRecords, declared, component, digest } = input;
|
|
169
|
+
const { from, to, sourceRecords, declared, component, digest, pins } = input;
|
|
134
170
|
if (from === to) return { error: `--from and --to name the same environment ("${from}")` };
|
|
135
171
|
if (digest !== undefined && component === undefined) {
|
|
136
172
|
return { error: "--digest picks one release of one component, so it needs --component <name>" };
|
|
137
173
|
}
|
|
138
174
|
|
|
175
|
+
if (pins !== undefined && Object.keys(pins).length > 0) {
|
|
176
|
+
if (component !== undefined) {
|
|
177
|
+
return { error: "--digest <component>=<digest> names its own component, so it does not take --component" };
|
|
178
|
+
}
|
|
179
|
+
const items: PromotionItem[] = [];
|
|
180
|
+
for (const [name, pinned] of Object.entries(pins).sort(([a], [b]) => a.localeCompare(b))) {
|
|
181
|
+
if (!declared.includes(name)) return { error: `component "${name}" is not declared in this checkout` };
|
|
182
|
+
const picked = selectRelease(sourceRecords, name, from, pinned);
|
|
183
|
+
if ("error" in picked) return picked;
|
|
184
|
+
items.push({ component: name, digest: picked.record.digest, source: picked.record });
|
|
185
|
+
}
|
|
186
|
+
return { from, to, items, notPromoted: [] };
|
|
187
|
+
}
|
|
188
|
+
|
|
139
189
|
if (component !== undefined) {
|
|
140
190
|
if (!declared.includes(component)) {
|
|
141
191
|
return { error: `component "${component}" is not declared in this checkout` };
|
|
@@ -197,6 +247,36 @@ export function planRollback(input: {
|
|
|
197
247
|
return { from: env, to: env, items: [{ component, digest: target.digest, source: target }], notPromoted: [] };
|
|
198
248
|
}
|
|
199
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Decide what a redeploy deploys: the release of `component` the ledger in
|
|
252
|
+
* `env` records as current. A deploy that fails partway records nothing, so
|
|
253
|
+
* after one the ledger still names the last good release while the
|
|
254
|
+
* environment may be running part of the failed one. `digest`, when given,
|
|
255
|
+
* has to be that current digest. It guards against a deploy recorded since
|
|
256
|
+
* the caller last looked; any earlier release is a rollback.
|
|
257
|
+
*/
|
|
258
|
+
export function planRedeploy(input: {
|
|
259
|
+
env: string;
|
|
260
|
+
records: ReleaseRecord[];
|
|
261
|
+
declared: string[];
|
|
262
|
+
component: string;
|
|
263
|
+
digest?: string;
|
|
264
|
+
}): PromotionPlan | { error: string } {
|
|
265
|
+
const { env, records, declared, component, digest } = input;
|
|
266
|
+
if (!declared.includes(component)) {
|
|
267
|
+
return { error: `component "${component}" is not declared in this checkout` };
|
|
268
|
+
}
|
|
269
|
+
const current = latestPerComponent(records.filter((r) => r.component === component)).get(component);
|
|
270
|
+
if (!current) return { error: `no release of "${component}" is recorded in "${env}"` };
|
|
271
|
+
if (digest !== undefined && digest !== current.digest) {
|
|
272
|
+
return {
|
|
273
|
+
error: `${digest} is not the current release of "${component}" in "${env}" (that is ${current.digest}); ` +
|
|
274
|
+
`use \`chant components rollback ${env} --component ${component} --digest ${digest}\` to restore an earlier release`,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
return { from: env, to: env, items: [{ component, digest: current.digest, source: current }], notPromoted: [] };
|
|
278
|
+
}
|
|
279
|
+
|
|
200
280
|
function isGate(step: DriverStep | DriverGate | DriverPhase): step is DriverGate {
|
|
201
281
|
return (step as { kind?: unknown }).kind === "gate";
|
|
202
282
|
}
|
|
@@ -243,6 +323,17 @@ export function promoteArchivePaths(component: DriverComponent): string[] {
|
|
|
243
323
|
return paths;
|
|
244
324
|
}
|
|
245
325
|
|
|
326
|
+
/**
|
|
327
|
+
* Whether `component`'s deploy has a publish step (#2602). Only such a
|
|
328
|
+
* component records a release when it deploys, so only its release can be
|
|
329
|
+
* promoted; a generated pipeline pins exactly these components' digests.
|
|
330
|
+
*/
|
|
331
|
+
export function hasPublishStep(component: DriverComponent): boolean {
|
|
332
|
+
const walk = (steps: Array<DriverStep | DriverGate | DriverPhase>): boolean =>
|
|
333
|
+
steps.some((step) => isPhase(step) ? walk(step.steps) : !isGate(step) && PUBLISH_STEP_KINDS.includes(step.kind));
|
|
334
|
+
return walk(component.deploy);
|
|
335
|
+
}
|
|
336
|
+
|
|
246
337
|
/** A component's composition with its build-time steps taken out. */
|
|
247
338
|
export interface PromotableComponent {
|
|
248
339
|
component: DriverComponent;
|
|
@@ -270,7 +361,7 @@ export const recordedDigestCapability: Capability<{ digest: string }, { digest:
|
|
|
270
361
|
* check that what is left can be pinned to one recorded digest. A phase left
|
|
271
362
|
* with nothing to run is dropped. `rollback` phases are untouched.
|
|
272
363
|
*
|
|
273
|
-
* With `pinDigest` (a rollback, #2531), the publish step is replaced too, by a
|
|
364
|
+
* With `pinDigest` (a rollback, #2531, or a redeploy, #2604), the publish step is replaced too, by a
|
|
274
365
|
* {@link RECORDED_DIGEST_KIND} step carrying that digest: the environment
|
|
275
366
|
* already received the artifact when the release being restored was
|
|
276
367
|
* deployed, and the archive it came from is usually long gone. Only the
|
|
@@ -279,9 +370,9 @@ export const recordedDigestCapability: Capability<{ digest: string }, { digest:
|
|
|
279
370
|
*/
|
|
280
371
|
export function withoutBuildSteps(
|
|
281
372
|
component: DriverComponent,
|
|
282
|
-
opts: { pinDigest?: string } = {},
|
|
373
|
+
opts: { pinDigest?: string; verb?: string } = {},
|
|
283
374
|
): PromotableComponent | { error: string } {
|
|
284
|
-
const verb = opts.pinDigest === undefined ? "a promote" : "a rollback";
|
|
375
|
+
const verb = opts.verb ?? (opts.pinDigest === undefined ? "a promote" : "a rollback");
|
|
285
376
|
const removed: string[] = [];
|
|
286
377
|
/** Phases a build step was removed from, and whether anything else is left in them. */
|
|
287
378
|
const emptied = new Map<string, boolean>();
|
|
@@ -466,6 +557,9 @@ export async function runPromotion(options: RunPromotionOptions): Promise<Driver
|
|
|
466
557
|
return await runInterpretDriver(targets, checkPublishedDigest(options.registry, expected), {
|
|
467
558
|
env: plan.to,
|
|
468
559
|
componentOutputs: options.componentOutputs ?? {},
|
|
560
|
+
// #2574: a gate approval for this promote or rollback is bound to the
|
|
561
|
+
// release it deploys, as well as the environment and composition.
|
|
562
|
+
releases: expected,
|
|
469
563
|
...(options.onProgress ? { onProgress: options.onProgress } : {}),
|
|
470
564
|
...(options.gates ? { gates: options.gates } : {}),
|
|
471
565
|
...(options.now ? { now: options.now } : {}),
|
|
@@ -485,7 +579,7 @@ export function gateApprover(result: DriverComponentResult | undefined): string
|
|
|
485
579
|
return approver;
|
|
486
580
|
}
|
|
487
581
|
|
|
488
|
-
/** What a promote or
|
|
582
|
+
/** What a promote, rollback or redeploy knows about its own run, for the records it writes. */
|
|
489
583
|
export interface DeployRunInfo {
|
|
490
584
|
runId: string;
|
|
491
585
|
runOrigin?: RunOrigin;
|
|
@@ -494,8 +588,8 @@ export interface DeployRunInfo {
|
|
|
494
588
|
approver?: string;
|
|
495
589
|
}
|
|
496
590
|
|
|
497
|
-
/** The fields a promote and a
|
|
498
|
-
function
|
|
591
|
+
/** The fields a promote, a rollback and a redeploy record share: the artifact is the earlier release's own. */
|
|
592
|
+
function sharedRecord(item: PromotionItem, env: string, run: DeployRunInfo): ReleaseRecordInput {
|
|
499
593
|
const { source } = item;
|
|
500
594
|
return {
|
|
501
595
|
component: item.component,
|
|
@@ -512,7 +606,7 @@ function redeployRecord(item: PromotionItem, env: string, run: DeployRunInfo): R
|
|
|
512
606
|
};
|
|
513
607
|
}
|
|
514
608
|
|
|
515
|
-
/** Name an earlier record the way `promotedFrom` and `
|
|
609
|
+
/** Name an earlier record the way `promotedFrom`, `restores` and `redeploys` do. */
|
|
516
610
|
function refTo(record: ReleaseRecord) {
|
|
517
611
|
return { env: record.env, runId: record.runId, timestamp: record.timestamp };
|
|
518
612
|
}
|
|
@@ -524,7 +618,7 @@ function refTo(record: ReleaseRecord) {
|
|
|
524
618
|
* release.
|
|
525
619
|
*/
|
|
526
620
|
export function promotionRecord(item: PromotionItem, to: string, run: DeployRunInfo): ReleaseRecordInput {
|
|
527
|
-
return { ...
|
|
621
|
+
return { ...sharedRecord(item, to, run), promotedFrom: refTo(item.source) };
|
|
528
622
|
}
|
|
529
623
|
|
|
530
624
|
/**
|
|
@@ -533,5 +627,15 @@ export function promotionRecord(item: PromotionItem, to: string, run: DeployRunI
|
|
|
533
627
|
* release.
|
|
534
628
|
*/
|
|
535
629
|
export function rollbackRecord(item: PromotionItem, env: string, run: DeployRunInfo): ReleaseRecordInput {
|
|
536
|
-
return { ...
|
|
630
|
+
return { ...sharedRecord(item, env, run), restores: refTo(item.source) };
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* The release record a successful redeploy appends. It carries the current
|
|
635
|
+
* release's artifact identities, as a rollback's carries the restored one's;
|
|
636
|
+
* `redeploys` names that release. The environment's current digest does not
|
|
637
|
+
* change, and the record says when it was last put back.
|
|
638
|
+
*/
|
|
639
|
+
export function redeployRecord(item: PromotionItem, env: string, run: DeployRunInfo): ReleaseRecordInput {
|
|
640
|
+
return { ...sharedRecord(item, env, run), redeploys: refTo(item.source) };
|
|
537
641
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { join, resolve } from "node:path";
|
|
2
2
|
import { params as currentBuildParams } from "../../params";
|
|
3
|
+
import { sendFunctionSource } from "../../discovery/sandbox/driver";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* chant #1051 — generates the source of the "driver" module that runs INSIDE
|
|
@@ -60,15 +61,14 @@ export function generateComponentDriverSource(options: GenerateComponentDriverOp
|
|
|
60
61
|
`import { collectComponents } from ${lit(DISCOVER_MODULE)};`,
|
|
61
62
|
`import { classifyChildError } from ${lit(CHILD_ERRORS_MODULE)};`,
|
|
62
63
|
`import { setBuildParams } from ${lit(PARAMS_MODULE)};`,
|
|
64
|
+
`import { writeSync } from "node:fs";`,
|
|
63
65
|
``,
|
|
64
66
|
// chant #1108 — snapshot of the parent's resolved build-time parameter
|
|
65
67
|
// values (scalars only), bound before any component file is imported.
|
|
66
68
|
`setBuildParams(${lit({ ...currentBuildParams })});`,
|
|
67
69
|
``,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
` else console.log(JSON.stringify(payload));`,
|
|
71
|
-
`}`,
|
|
70
|
+
// chant#2461 — awaits the send and fails loudly if it cannot be delivered.
|
|
71
|
+
...sendFunctionSource(),
|
|
72
72
|
``,
|
|
73
73
|
`async function main() {`,
|
|
74
74
|
` const modules = [];`,
|
|
@@ -114,12 +114,12 @@ export function generateComponentDriverSource(options: GenerateComponentDriverOp
|
|
|
114
114
|
` for (const [name, discovered] of collected.components) {`,
|
|
115
115
|
` components.push({ name, component: discovered.component, exportName: discovered.exportName, filePath: discovered.filePath });`,
|
|
116
116
|
` }`,
|
|
117
|
-
` send({ components, errors });`,
|
|
117
|
+
` await send({ components, errors });`,
|
|
118
118
|
`}`,
|
|
119
119
|
``,
|
|
120
|
-
`main().catch((err)
|
|
121
|
-
` send({ components: [], errors: [classifyChildError("", err).toJSON()], fatal: true })
|
|
122
|
-
`
|
|
120
|
+
`main().catch((err) =>`,
|
|
121
|
+
` send({ components: [], errors: [classifyChildError("", err).toJSON()], fatal: true }),`,
|
|
122
|
+
`);`,
|
|
123
123
|
);
|
|
124
124
|
|
|
125
125
|
return lines.join("\n");
|
|
@@ -3,6 +3,7 @@ import { realpathSync, rmSync } from "node:fs";
|
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { bundleDriver } from "../../discovery/sandbox/bundle";
|
|
5
5
|
import { classifyChildError } from "../../discovery/sandbox/child-errors";
|
|
6
|
+
import { settleOnExitAfterChannelDrains } from "../../discovery/sandbox/fork";
|
|
6
7
|
import { DiscoveryError, type DiscoveryErrorType } from "../../errors";
|
|
7
8
|
import type { Component } from "../component";
|
|
8
9
|
import type { DiscoveredComponent } from "../discover";
|
|
@@ -163,7 +164,9 @@ function runChildProcess(
|
|
|
163
164
|
reject(err);
|
|
164
165
|
});
|
|
165
166
|
|
|
166
|
-
|
|
167
|
+
// chant#2461 — a payload can still be in the IPC socket when the exit is
|
|
168
|
+
// handled; see settleOnExitAfterChannelDrains.
|
|
169
|
+
settleOnExitAfterChannelDrains(child, (code, signal) => {
|
|
167
170
|
if (settled) return;
|
|
168
171
|
settled = true;
|
|
169
172
|
clearTimeout(timeout);
|
|
@@ -55,6 +55,8 @@
|
|
|
55
55
|
|
|
56
56
|
import type { ArtifactReproducibility, ProvenanceLink } from "./reproducibility";
|
|
57
57
|
import { defaultReproducibility } from "./reproducibility";
|
|
58
|
+
import { contentDigest } from "../../content-digest";
|
|
59
|
+
import { canonicalJson } from "../../effect-receipt";
|
|
58
60
|
|
|
59
61
|
export type BuildArchiveEntryKind = "image" | "template" | "asset" | "sbom";
|
|
60
62
|
|
|
@@ -129,6 +131,12 @@ export interface BuildArchiveManifest {
|
|
|
129
131
|
* changed contents, not merely a re-run.
|
|
130
132
|
*/
|
|
131
133
|
manifestDigest: string;
|
|
134
|
+
/**
|
|
135
|
+
* Set on read, never written: `["legacy-digest"]` when a manifest
|
|
136
|
+
* persisted on `chant/lifecycle` holds digests from the 32-bit hash chant
|
|
137
|
+
* used before real SHA-256 (#2514). See ../../lifecycle/legacy-digest.ts.
|
|
138
|
+
*/
|
|
139
|
+
flags?: Array<"legacy-digest">;
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
/**
|
|
@@ -147,30 +155,28 @@ const DEFAULT_MEDIA_TYPES: Record<BuildArchiveEntryKind, string> = {
|
|
|
147
155
|
};
|
|
148
156
|
|
|
149
157
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* digest their producing capability already computed (e.g. `docker-build`'s
|
|
158
|
-
* `executor.docker.build` digest).
|
|
158
|
+
* The content digest every entry chant computes itself carries: real
|
|
159
|
+
* SHA-256 over the UTF-8 bytes of its input (../../content-digest.ts,
|
|
160
|
+
* chant #2514). Callers that digest content that never goes through the
|
|
161
|
+
* `CloudExecutor` (a synthesized template's serialized bytes, see ./build.ts's
|
|
162
|
+
* `addArchiveTemplate`, or an SBOM) use it. Production `image`/`asset`
|
|
163
|
+
* entries instead carry the digest their producing capability already
|
|
164
|
+
* computed (e.g. `docker-build`'s `executor.docker.build` digest).
|
|
159
165
|
*/
|
|
160
|
-
export
|
|
161
|
-
let hash = 0;
|
|
162
|
-
for (let i = 0; i < input.length; i++) {
|
|
163
|
-
hash = (Math.imul(hash, 31) + input.charCodeAt(i)) | 0;
|
|
164
|
-
}
|
|
165
|
-
const hex = Math.abs(hash).toString(16).padStart(8, "0");
|
|
166
|
-
return `sha256:${hex.repeat(8).slice(0, 64)}`;
|
|
167
|
-
}
|
|
166
|
+
export { contentDigest };
|
|
168
167
|
|
|
169
|
-
/**
|
|
168
|
+
/**
|
|
169
|
+
* Compute the aggregate `manifestDigest` over a set of entries. Each entry
|
|
170
|
+
* contributes its `kind`, `path` and `digest`; entries are sorted by path
|
|
171
|
+
* (UTF-16 code units, the order `canonicalJson` sorts keys in) so entry order
|
|
172
|
+
* never changes the digest, only content does. The list is hashed as
|
|
173
|
+
* `canonicalJson`, so no path or digest can be read as a field separator.
|
|
174
|
+
*/
|
|
170
175
|
export function computeManifestDigest(contents: BuildArchiveEntry[]): string {
|
|
171
|
-
const sorted =
|
|
172
|
-
|
|
173
|
-
|
|
176
|
+
const sorted = contents
|
|
177
|
+
.map((e) => ({ kind: e.kind, path: e.path, digest: e.digest }))
|
|
178
|
+
.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
179
|
+
return contentDigest(canonicalJson(sorted));
|
|
174
180
|
}
|
|
175
181
|
|
|
176
182
|
/** Start a new, empty manifest for `component`. Entries are added with `addArchiveEntry`. */
|
|
@@ -237,7 +237,7 @@
|
|
|
237
237
|
* `externalParameters`, never replacing it), not a shape break.
|
|
238
238
|
*/
|
|
239
239
|
|
|
240
|
-
import {
|
|
240
|
+
import { contentDigest } from "../../content-digest";
|
|
241
241
|
import type { Capability, DeployContext } from "../capability";
|
|
242
242
|
import type { ProvenanceLink } from "./reproducibility";
|
|
243
243
|
import type { BuildArchiveEntry } from "./build-archive";
|
|
@@ -555,10 +555,8 @@ export function createRunAgentCapability(
|
|
|
555
555
|
const PROMPT_PATH = "/work/prompt";
|
|
556
556
|
const OUTPUT_PATH = "/work/output";
|
|
557
557
|
|
|
558
|
-
/** `sha256:<hex>` over a string
|
|
559
|
-
|
|
560
|
-
return `sha256:${createHash("sha256").update(content, "utf8").digest("hex")}`;
|
|
561
|
-
}
|
|
558
|
+
/** `sha256:<hex>` over a string: ../../content-digest.ts's `contentDigest`, the same digest ./build-archive.ts uses. */
|
|
559
|
+
const sha256Digest = contentDigest;
|
|
562
560
|
|
|
563
561
|
const EMPTY_DIGEST = sha256Digest("");
|
|
564
562
|
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chant#2591 — the static reader of a config's `lexicons`: what it reads, and
|
|
3
|
+
* what it reports as unreadable instead of running the config.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
6
|
+
import { existsSync, mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { readConfigFieldsStatically, readLexiconDeclarationsStatically, unknownPathLexiconsNotice } from "./config-static";
|
|
10
|
+
|
|
11
|
+
let dir: string;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
dir = realpathSync(mkdtempSync(join(tmpdir(), "chant-2591-static-")));
|
|
15
|
+
// A project boundary, so the upward walk stops here.
|
|
16
|
+
writeFileSync(join(dir, "package.json"), "{}");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
rmSync(dir, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
function readTs(source: string) {
|
|
24
|
+
writeFileSync(join(dir, "chant.config.ts"), source);
|
|
25
|
+
return readLexiconDeclarationsStatically(dir);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const SITE = { name: "site", module: "./lexicon/index.ts" };
|
|
29
|
+
|
|
30
|
+
describe("readable configs", () => {
|
|
31
|
+
test.each([
|
|
32
|
+
["an object literal default export", `export default { lexicons: ["aws", { name: "site", module: "./lexicon/index.ts" }] };`],
|
|
33
|
+
[
|
|
34
|
+
"satisfies and a type import",
|
|
35
|
+
`import type { ChantConfig } from "@intentius/chant";\nexport default { lexicons: ["aws", { name: "site", module: "./lexicon/index.ts" }] } satisfies ChantConfig;`,
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
"a const alias",
|
|
39
|
+
`const config = { lexicons: ["aws", { name: "site", module: "./lexicon/index.ts" }], sourceDir: "src" };\nexport default config;`,
|
|
40
|
+
],
|
|
41
|
+
["a named config export", `export const config = { lexicons: ["aws", { name: "site", module: "./lexicon/index.ts" }] };`],
|
|
42
|
+
["named exports", `export const lexicons = ["aws", { name: "site", module: "./lexicon/index.ts" }];\nexport const sourceDir = "src";`],
|
|
43
|
+
[
|
|
44
|
+
"consts, a spread and a template",
|
|
45
|
+
`const dir = "lexicon";\nconst base = ["aws"];\nexport default { lexicons: [...base, { name: "site", module: \`./\${dir}/index.ts\` }] };`,
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
"a spread config",
|
|
49
|
+
`const shared = { lexicons: ["aws", { name: "site", module: "./lexicon/index.ts" }] };\nexport default { ...shared, sourceDir: "src" };`,
|
|
50
|
+
],
|
|
51
|
+
[
|
|
52
|
+
"other keys that are not readable",
|
|
53
|
+
`import { plugin } from "./rules";\nexport default { lexicons: ["aws", { name: "site", module: "./lexicon/index.ts" }], lint: { plugins: [plugin()] } };`,
|
|
54
|
+
],
|
|
55
|
+
])("%s", (_label, source) => {
|
|
56
|
+
const read = readTs(source);
|
|
57
|
+
expect(read).toEqual({ status: "read", configPath: join(dir, "chant.config.ts"), entries: ["aws", SITE] });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("a same-file function the fold subset covers is interpreted, not run", () => {
|
|
61
|
+
const read = readTs(`const pick = (extra) => ["aws", extra];\nexport default { lexicons: pick("k8s") };`);
|
|
62
|
+
expect(read).toMatchObject({ status: "read", entries: ["aws", "k8s"] });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("a config with no lexicons reads as none", () => {
|
|
66
|
+
expect(readTs(`export default { sourceDir: "src" };`)).toMatchObject({ status: "read", entries: [] });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("chant.config.json is parsed", () => {
|
|
70
|
+
writeFileSync(join(dir, "chant.config.json"), JSON.stringify({ lexicons: ["aws", SITE] }));
|
|
71
|
+
expect(readLexiconDeclarationsStatically(dir)).toMatchObject({ status: "read", entries: ["aws", SITE] });
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("the walk goes up from a subdirectory", () => {
|
|
75
|
+
writeFileSync(join(dir, "chant.config.ts"), `export default { lexicons: [{ name: "site", module: "./lexicon/index.ts" }] };`);
|
|
76
|
+
mkdirSync(join(dir, "src", "deep"), { recursive: true });
|
|
77
|
+
expect(readLexiconDeclarationsStatically(join(dir, "src", "deep"))).toMatchObject({ status: "read", entries: [SITE] });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("no config", () => {
|
|
81
|
+
expect(readLexiconDeclarationsStatically(dir)).toEqual({ status: "no-config" });
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("unreadable configs are reported, never run", () => {
|
|
86
|
+
test.each([
|
|
87
|
+
["an imported binding", `import { lexicons } from "./shared";\nexport default { lexicons };`, /lexicons/],
|
|
88
|
+
["process.env", `export default { lexicons: process.env.CI ? ["aws"] : ["aws", "k8s"] };`, /process/],
|
|
89
|
+
["a call as the default export", `export default defineConfig({ lexicons: ["aws"] });`, /not an object literal/],
|
|
90
|
+
["a re-export", `export { default } from "./base";`, /export/],
|
|
91
|
+
["an imported function in lexicons", `import { pick } from "./pick";\nexport default { lexicons: pick() };`, /line 4: .*pick/],
|
|
92
|
+
["entries of the wrong shape", `export default { lexicons: [{ name: "site" }] };`, /not a list of lexicon entries/],
|
|
93
|
+
])("%s", (_label, source, reason) => {
|
|
94
|
+
const marker = join(dir, "ran");
|
|
95
|
+
const read = readTs(`import { writeFileSync } from "node:fs";\nwriteFileSync(${JSON.stringify(marker)}, "x");\n${source}`);
|
|
96
|
+
expect(read.status).toBe("unknown");
|
|
97
|
+
expect(read.status === "unknown" && read.reason).toMatch(reason);
|
|
98
|
+
expect(unknownPathLexiconsNotice(read)).toContain("is unknown here and is treated as a package");
|
|
99
|
+
expect(existsSync(marker)).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("a readable config has no notice", () => {
|
|
103
|
+
expect(unknownPathLexiconsNotice(readTs(`export default { lexicons: ["aws"] };`))).toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("readConfigFieldsStatically (#2641)", () => {
|
|
108
|
+
test("reads the asked-for fields of a default export, a named config or named exports, and leaves unset ones out", () => {
|
|
109
|
+
writeFileSync(join(dir, "chant.config.ts"), `const env = "prod";\nexport default { environments: [env, { name: "staging" }], ownership: { stack: "shop", env }, lexicons: ["aws"] };`);
|
|
110
|
+
expect(readConfigFieldsStatically(dir, ["ownership", "environments", "sourceDir"])).toEqual({
|
|
111
|
+
status: "read",
|
|
112
|
+
configPath: join(dir, "chant.config.ts"),
|
|
113
|
+
fields: { ownership: { stack: "shop", env: "prod" }, environments: ["prod", { name: "staging" }] },
|
|
114
|
+
});
|
|
115
|
+
writeFileSync(join(dir, "chant.config.ts"), `export const ownership = { stack: "named" };`);
|
|
116
|
+
expect(readConfigFieldsStatically(dir, ["ownership"])).toMatchObject({ status: "read", fields: { ownership: { stack: "named" } } });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("a value it can't fold is unknown, with the reason, and the config is not run", () => {
|
|
120
|
+
const marker = join(dir, "ran");
|
|
121
|
+
writeFileSync(
|
|
122
|
+
join(dir, "chant.config.ts"),
|
|
123
|
+
`import { writeFileSync } from "node:fs";\nwriteFileSync(${JSON.stringify(marker)}, "x");\nexport default { ownership: { stack: process.env.STACK } };`,
|
|
124
|
+
);
|
|
125
|
+
const read = readConfigFieldsStatically(dir, ["ownership"]);
|
|
126
|
+
expect(read.status).toBe("unknown");
|
|
127
|
+
expect(existsSync(marker)).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("reads chant.config.json, and only in the directory itself", () => {
|
|
131
|
+
writeFileSync(join(dir, "chant.config.json"), JSON.stringify({ environments: ["prod"] }));
|
|
132
|
+
expect(readConfigFieldsStatically(dir, ["environments"])).toMatchObject({ status: "read", fields: { environments: ["prod"] } });
|
|
133
|
+
mkdirSync(join(dir, "sub"));
|
|
134
|
+
expect(readConfigFieldsStatically(join(dir, "sub"), ["environments"])).toEqual({ status: "no-config" });
|
|
135
|
+
});
|
|
136
|
+
});
|