@intentius/chant 0.124.46 → 0.124.48
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/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
- package/dist/__golden__/wave-loop/op-by-apply-hold.json +10 -0
- package/dist/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
- package/dist/__golden__/wave-loop/op-by-apply-pass.json +10 -0
- package/dist/__golden__/wave-loop/op-by-apply-skip.json +10 -0
- package/dist/__golden__/wave-loop/op-cost-member-clash.json +6 -0
- package/dist/__golden__/wave-loop/op-cost-member.json +13 -0
- package/dist/__golden__/wave-loop/op-denied-source.json +16 -0
- package/dist/__golden__/wave-loop/op-denied.json +16 -0
- package/dist/__golden__/wave-loop/op-discover-rest-short.json +6 -0
- package/dist/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
- package/dist/__golden__/wave-loop/op-discover-rest.json +26 -0
- package/dist/__golden__/wave-loop/op-discover-wide.json +26 -0
- package/dist/__golden__/wave-loop/op-held-on-again.json +16 -0
- package/dist/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
- package/dist/__golden__/wave-loop/op-held-on.json +16 -0
- package/dist/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-held.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-pass.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-share.json +33 -0
- package/dist/__golden__/wave-loop/op-outcome-held.json +10 -0
- package/dist/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
- package/dist/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
- package/dist/__golden__/wave-loop/op-outcome-replan.json +10 -0
- package/dist/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
- package/dist/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
- package/dist/__golden__/wave-loop/op-overridden.json +16 -0
- package/dist/__golden__/wave-loop/op-per-run.json +20 -0
- package/dist/__golden__/wave-loop/op-point.json +13 -0
- package/dist/__golden__/wave-loop/op-pr-review.json +10 -0
- package/dist/__golden__/wave-loop/op-sealed.json +10 -0
- package/dist/__golden__/wave-loop/op-spend-change-only.json +10 -0
- package/dist/__golden__/wave-loop/op-spend.json +10 -0
- package/dist/__golden__/wave-loop/op-wave-commands.json +55 -0
- package/dist/__golden__/wave-loop/op-wide-refused.json +26 -0
- package/dist/__golden__/wave-loop/op-wide-waiting.json +29 -0
- package/dist/__golden__/wave-loop/op-wide.json +39 -0
- package/dist/action-pins.d.ts +61 -0
- package/dist/action-pins.d.ts.map +1 -0
- package/dist/action-pins.js +77 -0
- package/dist/cli/commands/carve.d.ts +3 -0
- package/dist/cli/commands/carve.d.ts.map +1 -1
- package/dist/cli/commands/carve.js +36 -7
- package/dist/cli/commands/import-report.d.ts +26 -0
- package/dist/cli/commands/import-report.d.ts.map +1 -0
- package/dist/cli/commands/import-report.js +65 -0
- package/dist/cli/commands/import.d.ts +11 -1
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/import.js +22 -3
- package/dist/cli/commands/migrate.d.ts +13 -1
- package/dist/cli/commands/migrate.d.ts.map +1 -1
- package/dist/cli/commands/migrate.js +134 -10
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +21 -2
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +6 -1
- package/dist/cli/handlers/ledger.d.ts +3 -0
- package/dist/cli/handlers/ledger.d.ts.map +1 -0
- package/dist/cli/handlers/ledger.js +55 -0
- package/dist/cli/handlers/migrate.d.ts.map +1 -1
- package/dist/cli/handlers/migrate.js +18 -11
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/misc.js +12 -1
- package/dist/cli/handlers/pr-env.js +2 -2
- package/dist/cli/handlers/pr.d.ts +15 -1
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +143 -39
- package/dist/cli/handlers/query.d.ts +7 -2
- package/dist/cli/handlers/query.d.ts.map +1 -1
- package/dist/cli/handlers/query.js +16 -4
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +6 -3
- package/dist/cli/handlers/search.d.ts +2 -4
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/handlers/search.js +13 -39
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +65 -14
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +36 -2
- package/dist/cli/registry.d.ts +14 -4
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/builders.d.ts +5 -1
- package/dist/components/builders.d.ts.map +1 -1
- package/dist/components/builders.js +5 -0
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.js +16 -1
- package/dist/components/capability-plugin.d.ts +14 -0
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/pr-hooks.d.ts +21 -1
- package/dist/components/pr-hooks.d.ts.map +1 -1
- package/dist/components/pr-hooks.js +72 -4
- package/dist/components/pr-pipeline.d.ts +45 -10
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +47 -14
- package/dist/components/pr-policy.d.ts +36 -0
- package/dist/components/pr-policy.d.ts.map +1 -1
- package/dist/components/pr-policy.js +36 -0
- package/dist/components/starter-plugin.d.ts +1 -0
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/starter-plugin.js +5 -1
- package/dist/components/verbs/index.d.ts +2 -0
- package/dist/components/verbs/index.d.ts.map +1 -1
- package/dist/components/verbs/index.js +2 -0
- package/dist/components/verbs/publish-artifact.d.ts +65 -0
- package/dist/components/verbs/publish-artifact.d.ts.map +1 -0
- package/dist/components/verbs/publish-artifact.js +74 -0
- package/dist/components/verbs/publish-image.d.ts +129 -0
- package/dist/components/verbs/publish-image.d.ts.map +1 -0
- package/dist/components/verbs/publish-image.js +145 -0
- package/dist/config-environments.d.ts +20 -0
- package/dist/config-environments.d.ts.map +1 -1
- package/dist/config-environments.js +12 -0
- package/dist/config.d.ts +9 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -2
- package/dist/gate-decision.d.ts +11 -2
- package/dist/gate-decision.d.ts.map +1 -1
- package/dist/gate-decision.js +15 -3
- package/dist/graph-ir.d.ts +25 -0
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-walk.d.ts +66 -0
- package/dist/graph-walk.d.ts.map +1 -0
- package/dist/graph-walk.js +70 -0
- package/dist/import/parser.d.ts +19 -0
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/lexicon.d.ts +71 -6
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +11 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +2 -5
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/ledger-read.d.ts +65 -0
- package/dist/lifecycle/ledger-read.d.ts.map +1 -0
- package/dist/lifecycle/ledger-read.js +160 -0
- package/dist/lifecycle/release-workstation.d.ts +7 -0
- package/dist/lifecycle/release-workstation.d.ts.map +1 -1
- package/dist/lifecycle/release-workstation.js +25 -0
- package/dist/lifecycle/run-ledger.d.ts +4 -3
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +1 -0
- package/dist/lint/discover.js +3 -1
- package/dist/lint/post-synth.d.ts +127 -1
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/lint/post-synth.js +84 -2
- package/dist/lint/rules/op/index.d.ts +1 -0
- package/dist/lint/rules/op/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.js +2 -0
- package/dist/lint/rules/op/ops019-workspace-op-target.d.ts +32 -0
- package/dist/lint/rules/op/ops019-workspace-op-target.d.ts.map +1 -0
- package/dist/lint/rules/op/ops019-workspace-op-target.js +63 -0
- package/dist/op/activities/activity-contracts.d.ts +5 -2
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.js +5 -2
- package/dist/op/activities/index.d.ts +2 -2
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/index.js +1 -1
- package/dist/op/activities/review-agent.d.ts +90 -4
- package/dist/op/activities/review-agent.d.ts.map +1 -1
- package/dist/op/activities/review-agent.js +217 -19
- package/dist/op/activities/work-evidence.d.ts +8 -1
- package/dist/op/activities/work-evidence.d.ts.map +1 -1
- package/dist/op/activities/work-evidence.js +1 -0
- package/dist/op/builders.d.ts +9 -5
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +9 -5
- package/dist/op/composites/apply-op.d.ts +29 -2
- package/dist/op/composites/apply-op.d.ts.map +1 -1
- package/dist/op/composites/apply-op.js +20 -1
- package/dist/op/gate-approval.d.ts +11 -5
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-approval.js +11 -6
- package/dist/op/gate-quorum.d.ts +100 -3
- package/dist/op/gate-quorum.d.ts.map +1 -1
- package/dist/op/gate-quorum.js +108 -6
- package/dist/op/gate-review.d.ts +9 -0
- package/dist/op/gate-review.d.ts.map +1 -1
- package/dist/op/gate-review.js +13 -0
- package/dist/op/gate.d.ts +44 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +25 -4
- package/dist/op/index.d.ts +3 -3
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/index.js +1 -1
- package/dist/op/local-executor.d.ts +38 -3
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +48 -6
- package/dist/op/op-waves-run.d.ts +26 -1
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +259 -13
- package/dist/op/op-waves.d.ts +72 -5
- package/dist/op/op-waves.d.ts.map +1 -1
- package/dist/op/op-waves.js +110 -15
- package/dist/op/runtime.d.ts +11 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/schedule-overdue.d.ts +16 -0
- package/dist/op/schedule-overdue.d.ts.map +1 -1
- package/dist/op/schedule-overdue.js +36 -0
- package/dist/op/stage-hooks.d.ts +9 -2
- package/dist/op/stage-hooks.d.ts.map +1 -1
- package/dist/op/stage-hooks.js +7 -5
- package/dist/op/workspace-run-env.d.ts +22 -0
- package/dist/op/workspace-run-env.d.ts.map +1 -0
- package/dist/op/workspace-run-env.js +21 -0
- package/dist/pr-forge.d.ts +24 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +47 -0
- package/dist/pr-post.d.ts +42 -0
- package/dist/pr-post.d.ts.map +1 -1
- package/dist/pr-post.js +67 -1
- package/dist/read-filter.d.ts +65 -0
- package/dist/read-filter.d.ts.map +1 -0
- package/dist/read-filter.js +179 -0
- package/dist/run-event.d.ts +1 -1
- package/dist/run-event.d.ts.map +1 -1
- package/dist/terraform/carve.d.ts +24 -3
- package/dist/terraform/carve.d.ts.map +1 -1
- package/dist/terraform/carve.js +32 -14
- package/dist/terraform/cuts.d.ts +97 -0
- package/dist/terraform/cuts.d.ts.map +1 -0
- package/dist/terraform/cuts.js +138 -0
- package/dist/terraform/folds.d.ts +26 -0
- package/dist/terraform/folds.d.ts.map +1 -0
- package/dist/terraform/folds.js +38 -0
- package/dist/terraform/graph.d.ts.map +1 -1
- package/dist/terraform/graph.js +10 -0
- package/dist/terraform/reach.d.ts +140 -0
- package/dist/terraform/reach.d.ts.map +1 -0
- package/dist/terraform/reach.js +170 -0
- package/dist/terraform/score.d.ts +14 -0
- package/dist/terraform/score.d.ts.map +1 -1
- package/dist/terraform/score.js +14 -31
- package/dist/terraform/state.d.ts.map +1 -1
- package/dist/terraform/state.js +4 -2
- package/dist/terraform/types.d.ts +8 -0
- package/dist/terraform/types.d.ts.map +1 -1
- package/dist/wave-blocked.d.ts +10 -1
- package/dist/wave-blocked.d.ts.map +1 -1
- package/dist/wave-blocked.js +31 -3
- package/dist/wave-runtime.mjs +263 -254
- package/dist/workspace/check.schema.json +11 -2
- package/dist/workspace/checks/ci-branch.d.ts +44 -0
- package/dist/workspace/checks/ci-branch.d.ts.map +1 -0
- package/dist/workspace/checks/ci-branch.js +133 -0
- package/dist/workspace/checks/links.d.ts +3 -0
- package/dist/workspace/checks/links.d.ts.map +1 -1
- package/dist/workspace/checks/links.js +12 -1
- package/dist/workspace/checks/org.d.ts +48 -0
- package/dist/workspace/checks/org.d.ts.map +1 -0
- package/dist/workspace/checks/org.js +156 -0
- package/dist/workspace/checks.d.ts +11 -1
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +59 -7
- package/dist/workspace/ci-branches.d.ts +16 -0
- package/dist/workspace/ci-branches.d.ts.map +1 -1
- package/dist/workspace/ci-branches.js +27 -0
- package/dist/workspace/ci-green-cli.js +7 -7
- package/dist/workspace/ci-green-workflow.d.ts +1 -1
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +13 -8
- package/dist/workspace/ci-green.d.ts +14 -0
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +44 -11
- package/dist/workspace/ci-run.d.ts +9 -1
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +67 -6
- package/dist/workspace/ci-serve.d.ts +9 -0
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +114 -76
- package/dist/workspace/compose-graph.d.ts +14 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/composites.d.ts +4 -1
- package/dist/workspace/composites.d.ts.map +1 -1
- package/dist/workspace/composites.js +5 -2
- package/dist/workspace/composites.schema.json +22 -0
- package/dist/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
- package/dist/workspace/cross-root-lint.d.ts +56 -0
- package/dist/workspace/cross-root-lint.d.ts.map +1 -0
- package/dist/workspace/cross-root-lint.js +111 -0
- package/dist/workspace/dbt-models-reader.d.ts +44 -0
- package/dist/workspace/dbt-models-reader.d.ts.map +1 -0
- package/dist/workspace/dbt-models-reader.js +356 -0
- package/dist/workspace/dbt-project-common.d.ts +40 -0
- package/dist/workspace/dbt-project-common.d.ts.map +1 -0
- package/dist/workspace/dbt-project-common.js +106 -0
- package/dist/workspace/dbt-project-reader.d.ts +4 -3
- package/dist/workspace/dbt-project-reader.d.ts.map +1 -1
- package/dist/workspace/dbt-project-reader.js +14 -108
- package/dist/workspace/decision-rank.d.ts +26 -0
- package/dist/workspace/decision-rank.d.ts.map +1 -0
- package/dist/workspace/decision-rank.js +16 -0
- package/dist/workspace/declaration.d.ts +43 -2
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +45 -11
- package/dist/workspace/declaration.schema.json +133 -24
- package/dist/workspace/environments.d.ts +26 -2
- package/dist/workspace/environments.d.ts.map +1 -1
- package/dist/workspace/environments.js +26 -2
- package/dist/workspace/flyway-dir.d.ts +39 -0
- package/dist/workspace/flyway-dir.d.ts.map +1 -0
- package/dist/workspace/flyway-dir.js +104 -0
- package/dist/workspace/flyway-layout.d.ts +96 -0
- package/dist/workspace/flyway-layout.d.ts.map +1 -0
- package/dist/workspace/flyway-layout.js +308 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/graph-cli.js +26 -8
- package/dist/workspace/graph.schema.json +34 -0
- package/dist/workspace/intent.d.ts +2 -3
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +5 -9
- package/dist/workspace/kind-link-readers.d.ts +59 -2
- package/dist/workspace/kind-link-readers.d.ts.map +1 -1
- package/dist/workspace/kind-link-readers.js +17 -1
- package/dist/workspace/kinds.d.ts +66 -2
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/kinds.js +98 -4
- package/dist/workspace/ledger.schema.json +158 -0
- package/dist/workspace/lexicon-outputs.d.ts +20 -1
- package/dist/workspace/lexicon-outputs.d.ts.map +1 -1
- package/dist/workspace/lexicon-outputs.js +102 -10
- package/dist/workspace/lineage-check.d.ts +3 -0
- package/dist/workspace/lineage-check.d.ts.map +1 -1
- package/dist/workspace/lineage-check.js +23 -4
- package/dist/workspace/links.d.ts +8 -1
- package/dist/workspace/links.d.ts.map +1 -1
- package/dist/workspace/links.js +25 -1
- package/dist/workspace/ls.d.ts +8 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +7 -1
- package/dist/workspace/ls.schema.json +7 -0
- package/dist/workspace/member-commands.d.ts.map +1 -1
- package/dist/workspace/member-commands.js +54 -5
- package/dist/workspace/migrate-links.d.ts +45 -0
- package/dist/workspace/migrate-links.d.ts.map +1 -0
- package/dist/workspace/migrate-links.js +100 -0
- package/dist/workspace/migration-dirs.d.ts +74 -0
- package/dist/workspace/migration-dirs.d.ts.map +1 -0
- package/dist/workspace/migration-dirs.js +254 -0
- package/dist/workspace/reason-codes.d.ts +8 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +11 -0
- package/dist/workspace/records-cli.d.ts +11 -0
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-cli.js +57 -5
- package/dist/workspace/records.schema.json +5 -0
- package/dist/workspace/release-candidates.d.ts +2 -0
- package/dist/workspace/release-candidates.d.ts.map +1 -1
- package/dist/workspace/release-candidates.js +5 -2
- package/dist/workspace/release-train.d.ts +53 -0
- package/dist/workspace/release-train.d.ts.map +1 -0
- package/dist/workspace/release-train.js +134 -0
- package/dist/workspace/release-workflow.d.ts.map +1 -1
- package/dist/workspace/release-workflow.js +4 -3
- package/dist/workspace/repo-ref.d.ts +45 -0
- package/dist/workspace/repo-ref.d.ts.map +1 -0
- package/dist/workspace/repo-ref.js +114 -0
- package/dist/workspace/run-event.schema.json +2 -2
- package/dist/workspace/runs-cli.d.ts +9 -0
- package/dist/workspace/runs-cli.d.ts.map +1 -1
- package/dist/workspace/runs-cli.js +48 -3
- package/dist/workspace/runs.schema.json +7 -0
- package/dist/workspace/search-contract.d.ts +1 -1
- package/dist/workspace/search-contract.d.ts.map +1 -1
- package/dist/workspace/status-decisions.d.ts +37 -0
- package/dist/workspace/status-decisions.d.ts.map +1 -0
- package/dist/workspace/status-decisions.js +147 -0
- package/dist/workspace/status-gates.d.ts +9 -0
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +4 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +14 -2
- package/dist/workspace/status.schema.json +6 -0
- package/dist/workspace/work-cli.d.ts.map +1 -1
- package/dist/workspace/work-cli.js +5 -3
- package/dist/workspace/work-evidence-ops.d.ts +83 -0
- package/dist/workspace/work-evidence-ops.d.ts.map +1 -0
- package/dist/workspace/work-evidence-ops.js +169 -0
- package/dist/workspace/work-evidence.d.ts +12 -2
- package/dist/workspace/work-evidence.d.ts.map +1 -1
- package/dist/workspace/work-evidence.js +21 -3
- package/dist/workspace/work-evidence.schema.json +49 -2
- package/dist/workspace/workspace-gates.d.ts +110 -0
- package/dist/workspace/workspace-gates.d.ts.map +1 -0
- package/dist/workspace/workspace-gates.js +140 -0
- package/dist/workspace/workspace-kinds.schema.json +58 -3
- package/dist/workspace/workspace-ledger.d.ts +76 -0
- package/dist/workspace/workspace-ledger.d.ts.map +1 -0
- package/dist/workspace/workspace-ledger.js +91 -0
- package/dist/workspace/workspace-run.d.ts +112 -0
- package/dist/workspace/workspace-run.d.ts.map +1 -0
- package/dist/workspace/workspace-run.js +432 -0
- package/package.json +1 -1
- package/src/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
- package/src/__golden__/wave-loop/op-by-apply-hold.json +10 -0
- package/src/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
- package/src/__golden__/wave-loop/op-by-apply-pass.json +10 -0
- package/src/__golden__/wave-loop/op-by-apply-skip.json +10 -0
- package/src/__golden__/wave-loop/op-cost-member-clash.json +6 -0
- package/src/__golden__/wave-loop/op-cost-member.json +13 -0
- package/src/__golden__/wave-loop/op-denied-source.json +16 -0
- package/src/__golden__/wave-loop/op-denied.json +16 -0
- package/src/__golden__/wave-loop/op-discover-rest-short.json +6 -0
- package/src/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
- package/src/__golden__/wave-loop/op-discover-rest.json +26 -0
- package/src/__golden__/wave-loop/op-discover-wide.json +26 -0
- package/src/__golden__/wave-loop/op-held-on-again.json +16 -0
- package/src/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
- package/src/__golden__/wave-loop/op-held-on.json +16 -0
- package/src/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-held.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-pass.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-share.json +33 -0
- package/src/__golden__/wave-loop/op-outcome-held.json +10 -0
- package/src/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
- package/src/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
- package/src/__golden__/wave-loop/op-outcome-replan.json +10 -0
- package/src/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
- package/src/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
- package/src/__golden__/wave-loop/op-overridden.json +16 -0
- package/src/__golden__/wave-loop/op-per-run.json +20 -0
- package/src/__golden__/wave-loop/op-point.json +13 -0
- package/src/__golden__/wave-loop/op-pr-review.json +10 -0
- package/src/__golden__/wave-loop/op-sealed.json +10 -0
- package/src/__golden__/wave-loop/op-spend-change-only.json +10 -0
- package/src/__golden__/wave-loop/op-spend.json +10 -0
- package/src/__golden__/wave-loop/op-wave-commands.json +55 -0
- package/src/__golden__/wave-loop/op-wide-refused.json +26 -0
- package/src/__golden__/wave-loop/op-wide-waiting.json +29 -0
- package/src/__golden__/wave-loop/op-wide.json +39 -0
- package/src/action-pins.test.ts +78 -0
- package/src/action-pins.ts +92 -0
- package/src/cli/commands/carve-score-once.test.ts +99 -0
- package/src/cli/commands/carve.ts +37 -6
- package/src/cli/commands/check-lexicon.test.ts +2 -2
- package/src/cli/commands/import-report.ts +71 -0
- package/src/cli/commands/import.ts +33 -3
- package/src/cli/commands/migrate-dialect.test.ts +145 -0
- package/src/cli/commands/migrate.ts +132 -11
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +4 -4
- package/src/cli/handlers/approve.ts +23 -2
- package/src/cli/handlers/build.ts +7 -1
- package/src/cli/handlers/ledger.ts +54 -0
- package/src/cli/handlers/migrate.ts +21 -12
- package/src/cli/handlers/misc.ts +10 -1
- package/src/cli/handlers/pr-env.ts +2 -2
- package/src/cli/handlers/pr.ts +138 -36
- package/src/cli/handlers/query.ts +16 -4
- package/src/cli/handlers/release.ts +6 -3
- package/src/cli/handlers/search.ts +13 -34
- package/src/cli/main.test.ts +15 -0
- package/src/cli/main.ts +65 -14
- package/src/cli/parse-args.ts +27 -2
- package/src/cli/registry.ts +14 -4
- package/src/components/builders.ts +8 -0
- package/src/components/capability-plugin-loader.ts +13 -1
- package/src/components/capability-plugin.ts +16 -0
- package/src/components/pr-hooks.test.ts +73 -5
- package/src/components/pr-hooks.ts +95 -5
- package/src/components/pr-pipeline.ts +64 -16
- package/src/components/pr-policy.test.ts +64 -1
- package/src/components/pr-policy.ts +53 -0
- package/src/components/registry.test.ts +1 -1
- package/src/components/starter-plugin.ts +6 -0
- package/src/components/verbs/index.ts +2 -0
- package/src/components/verbs/publish-artifact.test.ts +60 -0
- package/src/components/verbs/publish-artifact.ts +122 -0
- package/src/components/verbs/publish-image.test.ts +101 -0
- package/src/components/verbs/publish-image.ts +265 -0
- package/src/config-environments.ts +28 -1
- package/src/config.ts +8 -1
- package/src/gate-decision-bundle.test.ts +1 -0
- package/src/gate-decision-identity.test.ts +9 -0
- package/src/gate-decision.ts +26 -3
- package/src/graph-ir.ts +27 -0
- package/src/graph-walk.test.ts +44 -0
- package/src/graph-walk.ts +102 -0
- package/src/import/parser.ts +20 -0
- package/src/lexicon.ts +74 -6
- package/src/lifecycle/gate-ledger.ts +8 -0
- package/src/lifecycle/git.ts +2 -2
- package/src/lifecycle/ledger-read.test.ts +107 -0
- package/src/lifecycle/ledger-read.ts +195 -0
- package/src/lifecycle/release-workstation.ts +24 -0
- package/src/lifecycle/run-ledger.ts +5 -2
- package/src/lint/cross-root.test.ts +122 -0
- package/src/lint/discover.ts +3 -1
- package/src/lint/post-synth.ts +212 -2
- package/src/lint/rules/op/index.ts +2 -0
- package/src/lint/rules/op/ops019-workspace-op-target.ts +89 -0
- package/src/op/activities/activity-contracts.ts +5 -2
- package/src/op/activities/index.ts +2 -2
- package/src/op/activities/review-agent.test.ts +221 -4
- package/src/op/activities/review-agent.ts +265 -22
- package/src/op/activities/work-evidence.ts +9 -1
- package/src/op/builders.ts +9 -5
- package/src/op/composites/apply-op.test.ts +108 -0
- package/src/op/composites/apply-op.ts +37 -2
- package/src/op/gate-approval.test.ts +6 -1
- package/src/op/gate-approval.ts +12 -6
- package/src/op/gate-quorum-at-base.test.ts +211 -0
- package/src/op/gate-quorum.ts +156 -8
- package/src/op/gate-review.ts +14 -0
- package/src/op/gate.ts +67 -8
- package/src/op/index.ts +3 -2
- package/src/op/local-executor.ts +69 -9
- package/src/op/op-waves-depends-on.test.ts +81 -0
- package/src/op/op-waves-hook-targets.test.ts +4 -2
- package/src/op/op-waves-quorum-product.test.ts +158 -0
- package/src/op/op-waves-run.ts +288 -14
- package/src/op/op-waves.ts +143 -14
- package/src/op/runtime.ts +8 -0
- package/src/op/schedule-overdue.ts +33 -0
- package/src/op/stage-hooks.ts +13 -5
- package/src/op/workspace-run-env.ts +29 -0
- package/src/pr-forge-bundle.test.ts +2 -2
- package/src/pr-forge.test.ts +47 -0
- package/src/pr-forge.ts +68 -0
- package/src/pr-post-open.e2e.test.ts +36 -0
- package/src/pr-post.test.ts +102 -3
- package/src/pr-post.ts +89 -1
- package/src/read-filter.test.ts +54 -0
- package/src/read-filter.ts +193 -0
- package/src/run-event.ts +1 -1
- package/src/stage-hooks-bundle.test.ts +1 -0
- package/src/terraform/carve.ts +52 -15
- package/src/terraform/cuts.test.ts +173 -0
- package/src/terraform/cuts.ts +188 -0
- package/src/terraform/folds.ts +44 -0
- package/src/terraform/graph.ts +9 -0
- package/src/terraform/radius.test.ts +90 -0
- package/src/terraform/reach.test.ts +146 -0
- package/src/terraform/reach.ts +247 -0
- package/src/terraform/score.ts +27 -29
- package/src/terraform/state.ts +3 -1
- package/src/terraform/types.ts +8 -0
- package/src/wave-blocked.ts +28 -3
- package/src/wave-loop-golden.test.ts +4 -3
- package/src/workspace/behold-kinds.test.ts +1 -1
- package/src/workspace/check-contract.test.ts +7 -6
- package/src/workspace/check.schema.json +11 -2
- package/src/workspace/checks/ci-branch.test.ts +128 -0
- package/src/workspace/checks/ci-branch.ts +141 -0
- package/src/workspace/checks/links.ts +12 -1
- package/src/workspace/checks/org.ts +169 -0
- package/src/workspace/checks.ts +64 -7
- package/src/workspace/ci-branches.ts +31 -0
- package/src/workspace/ci-green-branches.test.ts +153 -0
- package/src/workspace/ci-green-cli.ts +7 -7
- package/src/workspace/ci-green-workflow.test.ts +4 -3
- package/src/workspace/ci-green-workflow.ts +13 -9
- package/src/workspace/ci-green.ts +49 -12
- package/src/workspace/ci-run.test.ts +72 -1
- package/src/workspace/ci-run.ts +68 -8
- package/src/workspace/ci-serve.test.ts +95 -0
- package/src/workspace/ci-serve.ts +106 -65
- package/src/workspace/component-environments.test.ts +25 -2
- package/src/workspace/compose-graph.ts +9 -2
- package/src/workspace/composites.schema.json +22 -0
- package/src/workspace/composites.test.ts +39 -8
- package/src/workspace/composites.ts +6 -2
- package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
- package/src/workspace/cross-root-lint.test.ts +90 -0
- package/src/workspace/cross-root-lint.ts +126 -0
- package/src/workspace/dbt-kind.test.ts +3 -2
- package/src/workspace/dbt-models-reader.ts +362 -0
- package/src/workspace/dbt-models.test.ts +248 -0
- package/src/workspace/dbt-project-common.ts +113 -0
- package/src/workspace/dbt-project-reader.ts +29 -112
- package/src/workspace/decision-rank.ts +40 -0
- package/src/workspace/declaration.schema.json +133 -24
- package/src/workspace/declaration.ts +84 -15
- package/src/workspace/environments.ts +40 -4
- package/src/workspace/flyway-dir.ts +113 -0
- package/src/workspace/flyway-layout.ts +337 -0
- package/src/workspace/graph-cli.ts +25 -10
- package/src/workspace/graph-contract.test.ts +4 -2
- package/src/workspace/graph.schema.json +34 -0
- package/src/workspace/identity.test.ts +14 -2
- package/src/workspace/intent.ts +6 -14
- package/src/workspace/kind-link-readers.ts +68 -3
- package/src/workspace/kinds.test.ts +12 -7
- package/src/workspace/kinds.ts +132 -7
- package/src/workspace/ledger.schema.json +158 -0
- package/src/workspace/lexicon-outputs.ts +132 -13
- package/src/workspace/lineage-check.ts +30 -7
- package/src/workspace/links.ts +24 -1
- package/src/workspace/ls-contract.test.ts +1 -1
- package/src/workspace/ls.schema.json +7 -0
- package/src/workspace/ls.ts +10 -1
- package/src/workspace/member-commands.ts +60 -2
- package/src/workspace/member-file-schema.test.ts +124 -0
- package/src/workspace/migrate-links.test.ts +99 -0
- package/src/workspace/migrate-links.ts +108 -0
- package/src/workspace/migration-dirs.test.ts +272 -0
- package/src/workspace/migration-dirs.ts +267 -0
- package/src/workspace/org-scope.test.ts +238 -0
- package/src/workspace/reason-codes.test.ts +6 -1
- package/src/workspace/reason-codes.ts +11 -0
- package/src/workspace/records-cli.ts +58 -5
- package/src/workspace/records.schema.json +5 -0
- package/src/workspace/release-candidates.ts +7 -2
- package/src/workspace/release-train.ts +157 -0
- package/src/workspace/release-workflow.ts +4 -3
- package/src/workspace/repo-ref.ts +113 -0
- package/src/workspace/run-event.schema.json +2 -2
- package/src/workspace/runs-cli.ts +48 -4
- package/src/workspace/runs.schema.json +7 -0
- package/src/workspace/status-contract.test.ts +46 -1
- package/src/workspace/status-decisions.test.ts +100 -0
- package/src/workspace/status-decisions.ts +151 -0
- package/src/workspace/status-gates.ts +9 -0
- package/src/workspace/status.schema.json +6 -0
- package/src/workspace/status.ts +17 -3
- package/src/workspace/where-filters.test.ts +39 -0
- package/src/workspace/work-cli.ts +5 -3
- package/src/workspace/work-evidence-ops.test.ts +176 -0
- package/src/workspace/work-evidence-ops.ts +189 -0
- package/src/workspace/work-evidence.schema.json +49 -2
- package/src/workspace/work-evidence.ts +22 -3
- package/src/workspace/workspace-gates.ts +200 -0
- package/src/workspace/workspace-kinds.schema.json +58 -3
- package/src/workspace/workspace-ledger.ts +130 -0
- package/src/workspace/workspace-run.test.ts +322 -0
- package/src/workspace/workspace-run.ts +461 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Flyway project's layout, read from a workspace tree (#4400): its
|
|
3
|
+
* `flyway.toml` or `flyway.conf`, the locations it names, read recursively,
|
|
4
|
+
* and the placeholders it substitutes.
|
|
5
|
+
*
|
|
6
|
+
* This is the layout reading of sql-yodeler's `src/check/flyway.ts` (#250),
|
|
7
|
+
* moved here so the workspace kind and `yodel check` share one reader. It
|
|
8
|
+
* works on a {@link WorkspaceTree} and tree-relative paths instead of the
|
|
9
|
+
* file system, and it never runs Flyway or connects to a database.
|
|
10
|
+
*
|
|
11
|
+
* The configuration is the first `flyway.toml`, else `flyway.conf`, found in
|
|
12
|
+
* the directory checked or a directory above it, up to the tree's root. Its
|
|
13
|
+
* locations are read relative to the file's own directory:
|
|
14
|
+
*
|
|
15
|
+
* filesystem:<path> that directory
|
|
16
|
+
* classpath:<path> src/main/resources/<path> (and, for code
|
|
17
|
+
* <path> migrations, src/main/java/<path> and src/main/kotlin/<path>)
|
|
18
|
+
*
|
|
19
|
+
* With no configuration, or one that names no location, the directory
|
|
20
|
+
* checked is the one location. A location outside the tree is reported by
|
|
21
|
+
* {@link FlywayConfig.outside}, not read. A Java or Kotlin migration in a
|
|
22
|
+
* location is not SQL and is reported by {@link codeMigrations}.
|
|
23
|
+
*
|
|
24
|
+
* Placeholders are `${name}` (the prefix and suffix are configurable), from
|
|
25
|
+
* `flyway.placeholders.<name>` (conf) or `[flyway.placeholders]` (toml), plus
|
|
26
|
+
* Flyway's own: `flyway:defaultSchema`, `flyway:user`, `flyway:database`,
|
|
27
|
+
* `flyway:timestamp`, `flyway:filename`, `flyway:workingDirectory`,
|
|
28
|
+
* `flyway:table` and `flyway:environment`. {@link substitute} replaces them
|
|
29
|
+
* for a replay; the reader that lists migrations leaves the text as it is.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { posix } from "node:path";
|
|
33
|
+
import { joinPath, type WorkspaceTree } from "./tree";
|
|
34
|
+
|
|
35
|
+
export interface FlywayConfig {
|
|
36
|
+
/** The configuration file, tree-relative; undefined with none. */
|
|
37
|
+
file?: string;
|
|
38
|
+
/** Where file names are relative to: the configuration's directory, else the directory checked. Tree-relative. */
|
|
39
|
+
root: string;
|
|
40
|
+
/** The SQL locations, tree-relative directories. */
|
|
41
|
+
locations: string[];
|
|
42
|
+
/** Directories code migrations (Java, Kotlin) of a classpath location would be in. */
|
|
43
|
+
codeLocations: string[];
|
|
44
|
+
/** Locations the configuration names that lie outside the tree, as written. */
|
|
45
|
+
outside: string[];
|
|
46
|
+
placeholders: Record<string, string>;
|
|
47
|
+
placeholderPrefix: string;
|
|
48
|
+
placeholderSuffix: string;
|
|
49
|
+
placeholderReplacement: boolean;
|
|
50
|
+
sqlMigrationPrefix: string;
|
|
51
|
+
repeatableSqlMigrationPrefix: string;
|
|
52
|
+
baselineMigrationPrefix: string;
|
|
53
|
+
undoSqlMigrationPrefix: string;
|
|
54
|
+
sqlMigrationSeparator: string;
|
|
55
|
+
sqlMigrationSuffixes: string[];
|
|
56
|
+
defaultSchema?: string;
|
|
57
|
+
user?: string;
|
|
58
|
+
database?: string;
|
|
59
|
+
/** The JDBC URL, when the configuration has one. */
|
|
60
|
+
url?: string;
|
|
61
|
+
/** The dialect the URL names: `postgres` or `clickhouse`; undefined for another or none. */
|
|
62
|
+
dialect?: string;
|
|
63
|
+
table: string;
|
|
64
|
+
environment?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The first `flyway.toml`, else `flyway.conf`, in `dir` or a directory above it, up to the tree's root. */
|
|
68
|
+
export function findFlywayConfig(tree: WorkspaceTree, dir: string): string | undefined {
|
|
69
|
+
let at = dir;
|
|
70
|
+
for (;;) {
|
|
71
|
+
for (const name of ["flyway.toml", "flyway.conf"]) {
|
|
72
|
+
const f = joinPath(at, name);
|
|
73
|
+
if (tree.stat(f) === "file") return f;
|
|
74
|
+
}
|
|
75
|
+
if (at === "" || at === ".") return undefined;
|
|
76
|
+
at = posix.dirname(at) === "." ? "" : posix.dirname(at);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type Values = Record<string, string | string[] | boolean>;
|
|
81
|
+
|
|
82
|
+
/** Java-properties `flyway.*` keys, without the `flyway.` prefix. */
|
|
83
|
+
export function parseFlywayConf(text: string): Values {
|
|
84
|
+
const out: Values = {};
|
|
85
|
+
const lines = text.split(/\r?\n/);
|
|
86
|
+
for (let i = 0; i < lines.length; i++) {
|
|
87
|
+
let line = lines[i] ?? "";
|
|
88
|
+
while (/\\$/.test(line) && i + 1 < lines.length) line = line.slice(0, -1) + (lines[++i] ?? "").trimStart();
|
|
89
|
+
const t = line.trim();
|
|
90
|
+
if (t === "" || t.startsWith("#") || t.startsWith("!")) continue;
|
|
91
|
+
const m = /^([^=:\s]+)\s*[=:]\s*(.*)$/.exec(t);
|
|
92
|
+
if (!m) continue;
|
|
93
|
+
const key = m[1] ?? "";
|
|
94
|
+
if (!key.startsWith("flyway.")) continue;
|
|
95
|
+
out[key.slice("flyway.".length)] = (m[2] ?? "").trim();
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** A TOML document, as far as Flyway's configuration uses it: tables, strings, arrays of strings, booleans and numbers. */
|
|
101
|
+
export function parseToml(text: string): Record<string, Values> {
|
|
102
|
+
const tables: Record<string, Values> = { "": {} };
|
|
103
|
+
let current = "";
|
|
104
|
+
const lines = text.split(/\r?\n/);
|
|
105
|
+
for (let i = 0; i < lines.length; i++) {
|
|
106
|
+
const t = stripComment(lines[i] ?? "").trim();
|
|
107
|
+
if (t === "") continue;
|
|
108
|
+
const table = /^\[\s*([^\]]+?)\s*\]$/.exec(t);
|
|
109
|
+
if (table) {
|
|
110
|
+
current = (table[1] ?? "").split(".").map((k) => unquote(k.trim())).join(".");
|
|
111
|
+
tables[current] ??= {};
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const kv = /^("[^"]*"|'[^']*'|[A-Za-z0-9_.:-]+)\s*=\s*(.*)$/.exec(t);
|
|
115
|
+
if (!kv) continue;
|
|
116
|
+
let raw = kv[2] ?? "";
|
|
117
|
+
// A multi-line array runs to its closing bracket.
|
|
118
|
+
while (raw.trimStart().startsWith("[") && !balanced(raw) && i + 1 < lines.length) raw += ` ${stripComment(lines[++i] ?? "").trim()}`;
|
|
119
|
+
const path = unquote(kv[1] ?? "").split(".");
|
|
120
|
+
const key = path.pop() ?? "";
|
|
121
|
+
const where = [current, ...path].filter(Boolean).join(".");
|
|
122
|
+
tables[where] ??= {};
|
|
123
|
+
tables[where][key] = tomlValue(raw.trim());
|
|
124
|
+
}
|
|
125
|
+
return tables;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function stripComment(line: string): string {
|
|
129
|
+
let quote: string | undefined;
|
|
130
|
+
for (let i = 0; i < line.length; i++) {
|
|
131
|
+
const c = line[i];
|
|
132
|
+
if (quote) {
|
|
133
|
+
if (c === "\\" && quote === '"') i++;
|
|
134
|
+
else if (c === quote) quote = undefined;
|
|
135
|
+
} else if (c === '"' || c === "'") quote = c;
|
|
136
|
+
else if (c === "#") return line.slice(0, i);
|
|
137
|
+
}
|
|
138
|
+
return line;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const balanced = (s: string) => (s.match(/\[/g) ?? []).length <= (s.match(/\]/g) ?? []).length;
|
|
142
|
+
|
|
143
|
+
function unquote(s: string): string {
|
|
144
|
+
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) return s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
145
|
+
return s;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function tomlValue(raw: string): string | string[] | boolean {
|
|
149
|
+
if (raw === "true" || raw === "false") return raw === "true";
|
|
150
|
+
if (raw.startsWith("[")) {
|
|
151
|
+
const inner = raw.replace(/^\[/, "").replace(/\]\s*$/, "");
|
|
152
|
+
return (inner.match(/"(?:[^"\\]|\\.)*"|'[^']*'|[^,\s]+/g) ?? []).map((v) => unquote(v.trim())).filter((v) => v !== "");
|
|
153
|
+
}
|
|
154
|
+
return unquote(raw);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const list = (v: string | string[] | boolean | undefined): string[] | undefined =>
|
|
158
|
+
v === undefined || typeof v === "boolean" ? undefined : Array.isArray(v) ? v : v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
159
|
+
const str = (v: string | string[] | boolean | undefined): string | undefined => (typeof v === "string" ? v : Array.isArray(v) ? v.join(",") : undefined);
|
|
160
|
+
|
|
161
|
+
/** The database name a JDBC URL names: the path after the host. */
|
|
162
|
+
function databaseOf(url: string | undefined): string | undefined {
|
|
163
|
+
const m = url === undefined ? null : /^jdbc:[a-z0-9]+:\/\/[^/]+\/([^?;]+)/i.exec(url);
|
|
164
|
+
return m?.[1] ?? undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** The dialect a JDBC URL names. */
|
|
168
|
+
function dialectOf(url: string | undefined): string | undefined {
|
|
169
|
+
const m = url === undefined ? null : /^jdbc:([a-z0-9]+):/i.exec(url);
|
|
170
|
+
const driver = m?.[1]?.toLowerCase();
|
|
171
|
+
return driver === "postgresql" ? "postgres" : driver === "clickhouse" ? "clickhouse" : undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** The configuration `file` holds, or the defaults with `dir` as the one location when there is none. */
|
|
175
|
+
export function readFlywayConfig(tree: WorkspaceTree, file: string | undefined, dir: string): FlywayConfig {
|
|
176
|
+
let values: Values = {};
|
|
177
|
+
let placeholders: Record<string, string> = {};
|
|
178
|
+
if (file !== undefined) {
|
|
179
|
+
const text = tree.read(file);
|
|
180
|
+
if (file.endsWith(".toml")) {
|
|
181
|
+
const t = parseToml(text);
|
|
182
|
+
const flyway = t.flyway ?? {};
|
|
183
|
+
const name = str(flyway.environment) ?? "default";
|
|
184
|
+
const env = t[`environments.${name}`] ?? {};
|
|
185
|
+
values = { ...flyway, ...(t[`environments.${name}.flyway`] ?? {}), ...(env.url !== undefined ? { url: env.url } : {}), ...(env.user !== undefined ? { user: env.user } : {}), ...(env.schemas !== undefined ? { schemas: env.schemas } : {}) };
|
|
186
|
+
if (str(flyway.environment) !== undefined) values.environment = name;
|
|
187
|
+
placeholders = Object.fromEntries(Object.entries({ ...(t["flyway.placeholders"] ?? {}), ...(t[`environments.${name}.flyway.placeholders`] ?? {}) }).map(([k, v]) => [k, str(v) ?? String(v)]));
|
|
188
|
+
} else {
|
|
189
|
+
values = parseFlywayConf(text);
|
|
190
|
+
placeholders = Object.fromEntries(Object.entries(values).flatMap(([k, v]) => (k.startsWith("placeholders.") ? [[k.slice("placeholders.".length), str(v) ?? ""]] : [])));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const root = file === undefined ? dir : posix.dirname(file) === "." ? "" : posix.dirname(file);
|
|
194
|
+
const named = list(values.locations) ?? [];
|
|
195
|
+
const locations: string[] = [];
|
|
196
|
+
const codeLocations: string[] = [];
|
|
197
|
+
const outside: string[] = [];
|
|
198
|
+
const inside = (...parts: string[]): string | undefined => {
|
|
199
|
+
const p = posix.normalize(joinPath(...parts)) || ".";
|
|
200
|
+
if (p === ".." || p.startsWith("../") || posix.isAbsolute(p)) return undefined;
|
|
201
|
+
return p === "." ? "" : p;
|
|
202
|
+
};
|
|
203
|
+
for (const l of named) {
|
|
204
|
+
const m = /^(filesystem|classpath):(.*)$/.exec(l);
|
|
205
|
+
const kind = m?.[1] ?? "classpath";
|
|
206
|
+
const raw = m?.[2] ?? l;
|
|
207
|
+
if (kind === "filesystem") {
|
|
208
|
+
const at = posix.isAbsolute(raw) ? undefined : inside(root, raw);
|
|
209
|
+
if (at === undefined) outside.push(l);
|
|
210
|
+
else locations.push(at);
|
|
211
|
+
} else {
|
|
212
|
+
const path = raw.replace(/^\/+/, "");
|
|
213
|
+
const res = inside(root, "src/main/resources", path);
|
|
214
|
+
if (res === undefined) outside.push(l);
|
|
215
|
+
else {
|
|
216
|
+
locations.push(res);
|
|
217
|
+
for (const lang of ["java", "kotlin"]) {
|
|
218
|
+
const c = inside(root, "src/main", lang, path);
|
|
219
|
+
if (c !== undefined) codeLocations.push(c);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (named.length === 0) locations.push(dir);
|
|
225
|
+
const schemas = list(values.schemas);
|
|
226
|
+
const replacement = values.placeholderReplacement;
|
|
227
|
+
const defaultSchema = str(values.defaultSchema) ?? schemas?.[0];
|
|
228
|
+
const user = str(values.user);
|
|
229
|
+
const url = str(values.url);
|
|
230
|
+
const database = databaseOf(url);
|
|
231
|
+
const dialect = dialectOf(url);
|
|
232
|
+
const environment = str(values.environment);
|
|
233
|
+
return {
|
|
234
|
+
...(file !== undefined ? { file } : {}),
|
|
235
|
+
root,
|
|
236
|
+
locations,
|
|
237
|
+
codeLocations,
|
|
238
|
+
outside,
|
|
239
|
+
placeholders,
|
|
240
|
+
placeholderPrefix: str(values.placeholderPrefix) ?? "${",
|
|
241
|
+
placeholderSuffix: str(values.placeholderSuffix) ?? "}",
|
|
242
|
+
placeholderReplacement: !(replacement === false || replacement === "false"),
|
|
243
|
+
sqlMigrationPrefix: str(values.sqlMigrationPrefix) ?? "V",
|
|
244
|
+
repeatableSqlMigrationPrefix: str(values.repeatableSqlMigrationPrefix) ?? "R",
|
|
245
|
+
baselineMigrationPrefix: str(values.baselineMigrationPrefix) ?? "B",
|
|
246
|
+
undoSqlMigrationPrefix: str(values.undoSqlMigrationPrefix) ?? "U",
|
|
247
|
+
sqlMigrationSeparator: str(values.sqlMigrationSeparator) ?? "__",
|
|
248
|
+
sqlMigrationSuffixes: list(values.sqlMigrationSuffixes) ?? [".sql"],
|
|
249
|
+
...(defaultSchema !== undefined ? { defaultSchema } : {}),
|
|
250
|
+
...(user !== undefined ? { user } : {}),
|
|
251
|
+
...(database !== undefined ? { database } : {}),
|
|
252
|
+
...(url !== undefined ? { url } : {}),
|
|
253
|
+
...(dialect !== undefined ? { dialect } : {}),
|
|
254
|
+
table: str(values.table) ?? "flyway_schema_history",
|
|
255
|
+
...(environment !== undefined ? { environment } : {}),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Every file under `dir`, recursively, tree-relative, sorted; none when it does not exist. */
|
|
260
|
+
export function walk(tree: WorkspaceTree, dir: string): string[] {
|
|
261
|
+
const entries = tree.list(dir);
|
|
262
|
+
if (!entries) return [];
|
|
263
|
+
const out: string[] = [];
|
|
264
|
+
for (const e of [...entries].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
|
|
265
|
+
const p = joinPath(dir, e.name);
|
|
266
|
+
if (e.type === "dir") out.push(...walk(tree, p));
|
|
267
|
+
else out.push(p);
|
|
268
|
+
}
|
|
269
|
+
return out;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const base = (f: string): string => posix.basename(f);
|
|
273
|
+
|
|
274
|
+
/** A Flyway SQL callback (`beforeMigrate.sql`, `afterEachMigrate__notify.sql`, ...): run around a migrate, not a migration. */
|
|
275
|
+
export const isCallback = (file: string): boolean => /^(before|after)[A-Z][A-Za-z]*(__.*)?\.[A-Za-z]+$/.test(base(file));
|
|
276
|
+
|
|
277
|
+
/** The code migrations (Java, Kotlin) in `config`'s locations, relative to the tree. */
|
|
278
|
+
export function codeMigrations(tree: WorkspaceTree, config: FlywayConfig): string[] {
|
|
279
|
+
const code = /\.(java|kt|class)$/;
|
|
280
|
+
const prefixes = [config.sqlMigrationPrefix, config.repeatableSqlMigrationPrefix, config.baselineMigrationPrefix].filter(Boolean);
|
|
281
|
+
const migration = (f: string) => code.test(f) && prefixes.some((p) => base(f).startsWith(p)) && base(f).includes(config.sqlMigrationSeparator);
|
|
282
|
+
return [...new Set([...config.locations, ...config.codeLocations])].flatMap((d) => walk(tree, d)).filter(migration);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** The values Flyway gives its own placeholders, for `file`. */
|
|
286
|
+
function builtins(config: FlywayConfig, file: string, fallback: { defaultSchema?: string | undefined }): Record<string, string | undefined> {
|
|
287
|
+
return {
|
|
288
|
+
"flyway:defaultSchema": config.defaultSchema ?? fallback.defaultSchema,
|
|
289
|
+
"flyway:user": config.user,
|
|
290
|
+
"flyway:database": config.database,
|
|
291
|
+
"flyway:timestamp": new Date().toISOString().replace("T", " ").replace(/\..*$/, ""),
|
|
292
|
+
"flyway:filename": base(file),
|
|
293
|
+
"flyway:workingDirectory": process.cwd(),
|
|
294
|
+
"flyway:table": config.table,
|
|
295
|
+
"flyway:environment": config.environment,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const escape = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
300
|
+
|
|
301
|
+
/** A placeholder with no value; `file` is as the caller passed it, `line` is from 1. */
|
|
302
|
+
export class FlywayPlaceholderError extends Error {
|
|
303
|
+
constructor(
|
|
304
|
+
readonly file: string,
|
|
305
|
+
readonly line: number,
|
|
306
|
+
readonly placeholder: string,
|
|
307
|
+
readonly name: string,
|
|
308
|
+
readonly where: string,
|
|
309
|
+
) {
|
|
310
|
+
super(`${file}:${String(line)}: no value for the placeholder ${placeholder}; set it in ${where} (flyway.placeholders.${name} in flyway.conf, [flyway.placeholders] in flyway.toml)`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* `text` with its placeholders replaced, line by line so line numbers hold.
|
|
316
|
+
* A placeholder with no value throws a {@link FlywayPlaceholderError} naming
|
|
317
|
+
* `file` and the line, as Flyway's own run would fail.
|
|
318
|
+
*/
|
|
319
|
+
export function substitute(text: string, file: string, config: FlywayConfig, fallback: { defaultSchema?: string | undefined } = {}): string {
|
|
320
|
+
if (!config.placeholderReplacement) return text;
|
|
321
|
+
const values: Record<string, string | undefined> = { ...builtins(config, file, fallback), ...config.placeholders };
|
|
322
|
+
const lower = new Map(Object.entries(values).map(([k, v]) => [k.toLowerCase(), v]));
|
|
323
|
+
const pattern = new RegExp(`${escape(config.placeholderPrefix)}([A-Za-z0-9_.:-]+)${escape(config.placeholderSuffix)}`, "g");
|
|
324
|
+
return text
|
|
325
|
+
.split("\n")
|
|
326
|
+
.map((line, i) =>
|
|
327
|
+
line.replace(pattern, (whole, name: string) => {
|
|
328
|
+
const v = values[name] ?? lower.get(name.toLowerCase());
|
|
329
|
+
if (v === undefined) {
|
|
330
|
+
const where = config.file === undefined ? "a flyway.toml or flyway.conf beside the migrations" : config.file;
|
|
331
|
+
throw new FlywayPlaceholderError(file, i + 1, whole, name, where);
|
|
332
|
+
}
|
|
333
|
+
return v;
|
|
334
|
+
}),
|
|
335
|
+
)
|
|
336
|
+
.join("\n");
|
|
337
|
+
}
|
|
@@ -35,9 +35,10 @@ import { composedId, composeWorkspaceGraph, readMemberIr, type ComposedMember, t
|
|
|
35
35
|
import { isViewDocument, prefixView } from "../capabilities/view";
|
|
36
36
|
import { refusedCapabilityView } from "./nested-graph";
|
|
37
37
|
import { openGraphCache, planStamps, splitCached, storeReads, withoutUnits } from "./graph-cache";
|
|
38
|
-
import { readDeclaration, readerVersion, WORKSPACE_ERROR_CODES, WorkspaceReadError, type Declaration, type ErrorLocation } from "./declaration";
|
|
38
|
+
import { readDeclaration, readerVersion, WORKSPACE_ERROR_CODES, WorkspaceReadError, type Declaration, type ErrorLocation, type RepoRef } from "./declaration";
|
|
39
39
|
import { describePlan, emitDocument, executePlan, memberStatus, planJson, planMembers, type MemberArgv, type MemberPlan, type Toolchain, type UnitResult } from "./member-commands";
|
|
40
|
-
import { loadKindRegistry, type KindRegistry } from "./kinds";
|
|
40
|
+
import { loadKindRegistry, REPO_KIND, type KindRegistry } from "./kinds";
|
|
41
|
+
import { repoIdentity } from "./repo-ref";
|
|
41
42
|
import { readKindLinks } from "./kind-link-readers";
|
|
42
43
|
import { prepareKindReaders, type PreparedReaders } from "./kind-readers";
|
|
43
44
|
import { recordLinkRows } from "./record-assets";
|
|
@@ -335,10 +336,11 @@ export async function workspaceGraph(query: GraphQuery): Promise<GraphResult> {
|
|
|
335
336
|
}
|
|
336
337
|
// Links (#2539) resolve against the declaration that was read, the revision's for --at, and the kinds installed now.
|
|
337
338
|
// A kind's link reader (#3932) reads the member's files in the tree read, as check does.
|
|
338
|
-
const graph = composeWorkspaceGraph({ name: declaration.name, root: located.root }, inputs, { declaration, kinds, read: readKindLinks(declaration, located.tree, kinds) });
|
|
339
|
+
const graph = composeWorkspaceGraph({ name: declaration.name, root: located.root, repo: repoIdentity(located.rootOnDisk) }, inputs, { declaration, kinds, read: readKindLinks(declaration, located.tree, kinds) });
|
|
339
340
|
// The box's factory and listing (#3146), from the declaration read, so --at gives them at that revision.
|
|
340
341
|
for (const member of graph.members) member.box = graphBox(declaration.members.find((m) => m.name === member.name)?.box ?? null, located.tree, declaration.members);
|
|
341
342
|
// Nested workspaces (#2551, ws-071): read-only, through their own chant workspace graph.
|
|
343
|
+
// Repo members (#3168): another repository's workspace at its pinned revision, read the same way.
|
|
342
344
|
const nestedFailed = await expandNested(graph, declaration.members, query, located.rootOnDisk, located.at);
|
|
343
345
|
let recordsFailed = false;
|
|
344
346
|
if (query.kind !== undefined) {
|
|
@@ -365,33 +367,46 @@ export async function workspaceGraph(query: GraphQuery): Promise<GraphResult> {
|
|
|
365
367
|
|
|
366
368
|
/**
|
|
367
369
|
* Expand every member of kind `workspace` the query covers, read-only (#2551,
|
|
368
|
-
* ws-071)
|
|
370
|
+
* ws-071), and every member of kind `repo` the same way, at its pinned
|
|
371
|
+
* revision, fetched into the user's cache (#3168). Returns whether one could
|
|
372
|
+
* not be read.
|
|
369
373
|
*/
|
|
370
374
|
async function expandNested(
|
|
371
375
|
graph: WorkspaceGraph,
|
|
372
|
-
members: readonly { name: string; dir: string; kind: string }[],
|
|
376
|
+
members: readonly { name: string; dir: string; kind: string; repo: RepoRef | null }[],
|
|
373
377
|
query: GraphQuery,
|
|
374
378
|
rootOnDisk: string,
|
|
375
379
|
at: string | null,
|
|
376
380
|
): Promise<boolean> {
|
|
377
|
-
const
|
|
381
|
+
const covered = (m: { name: string }) => !query.members?.length || query.members.includes(m.name);
|
|
382
|
+
const nested = members.filter((m) => (m.kind === "workspace" || (m.kind === REPO_KIND && m.repo !== null)) && covered(m));
|
|
378
383
|
if (nested.length === 0) return false;
|
|
379
384
|
const { foldNested, readNested } = await import("./nested-graph");
|
|
385
|
+
const { fetchRepoAt } = await import("./repo-ref");
|
|
380
386
|
const reads = await Promise.all(
|
|
381
387
|
nested.map(async (m) => {
|
|
388
|
+
if (m.repo) {
|
|
389
|
+
// The other repository at its pinned revision, whatever revision the outer read is at.
|
|
390
|
+
const fetched = fetchRepoAt(m.repo, rootOnDisk);
|
|
391
|
+
if (!fetched.ok) return { m, read: null, missing: fetched.message };
|
|
392
|
+
if (!existsSync(fetched.dir)) return { m, read: null, missing: `${m.repo.url} at ${m.repo.rev} has no directory ${m.repo.workspace}` };
|
|
393
|
+
return { m, read: await readNested(fetched.dir, query.args ?? {}, m.repo.rev, GRAPH_OUTPUT_SCHEMA_ID, GRAPH_CONTRACT_VERSION, query.reader), missing: null };
|
|
394
|
+
}
|
|
382
395
|
const abs = join(rootOnDisk, ...m.dir.split("/"));
|
|
383
|
-
if (!existsSync(abs)) return { m, read: null };
|
|
384
|
-
return { m, read: await readNested(abs, query.args ?? {}, at, GRAPH_OUTPUT_SCHEMA_ID, GRAPH_CONTRACT_VERSION, query.reader) };
|
|
396
|
+
if (!existsSync(abs)) return { m, read: null, missing: null };
|
|
397
|
+
return { m, read: await readNested(abs, query.args ?? {}, at, GRAPH_OUTPUT_SCHEMA_ID, GRAPH_CONTRACT_VERSION, query.reader), missing: null };
|
|
385
398
|
}),
|
|
386
399
|
);
|
|
387
400
|
let failed = false;
|
|
388
|
-
for (const { m, read } of reads) {
|
|
401
|
+
for (const { m, read, missing } of reads) {
|
|
389
402
|
const member = graph.members.find((x) => x.name === m.name);
|
|
390
403
|
if (!member) continue;
|
|
404
|
+
if (m.repo) member.repo = { ...m.repo, read: read?.ok ? (read.doc.workspace.repo ?? null) : null };
|
|
391
405
|
if (!read) {
|
|
392
406
|
failed = true;
|
|
393
407
|
member.status = "failed";
|
|
394
|
-
|
|
408
|
+
// A repo that can't be fetched is a read that failed; the contract's codes stay as they are.
|
|
409
|
+
member.reason = missing !== null ? { code: "command-failed", message: missing } : { code: "dir-missing", message: `the nested workspace's directory ${m.dir} does not exist` };
|
|
395
410
|
continue;
|
|
396
411
|
}
|
|
397
412
|
if (read.stderr.trim() && query.onStderr) query.onStderr(read.stderr.endsWith("\n") ? read.stderr : `${read.stderr}\n`);
|
|
@@ -230,7 +230,9 @@ describe("chant workspace graph on the chant repo (#2557)", () => {
|
|
|
230
230
|
const g = result(doc);
|
|
231
231
|
expectValid(g);
|
|
232
232
|
expect(failed).toBe(false);
|
|
233
|
-
expect(g.workspace).
|
|
233
|
+
expect(g.workspace).toMatchObject({ name: "chant", root: "." });
|
|
234
|
+
// The repo identity (#3168) is this checkout's origin, whatever it is here.
|
|
235
|
+
expect(g.workspace.repo === null || typeof g.workspace.repo === "string").toBe(true);
|
|
234
236
|
expect(g.at).toBeNull();
|
|
235
237
|
expect(g.members.every((m) => m.status === "skipped" && m.reason?.code === "kind-not-run")).toBe(true);
|
|
236
238
|
});
|
|
@@ -294,7 +296,7 @@ describe("chant workspace graph on built workspaces", () => {
|
|
|
294
296
|
for (const at of [undefined, sha]) {
|
|
295
297
|
const g = result((await workspaceGraph({ cwd: join(top, "infra", "api"), at })).doc);
|
|
296
298
|
expectValid(g);
|
|
297
|
-
expect(g.workspace).toEqual({ name: "acme", root: "infra" });
|
|
299
|
+
expect(g.workspace).toEqual({ name: "acme", root: "infra", repo: null });
|
|
298
300
|
expect(g.nodes.map((n) => n.id)).toEqual(["api/Queue"]);
|
|
299
301
|
}
|
|
300
302
|
});
|
|
@@ -67,6 +67,13 @@
|
|
|
67
67
|
"type": "string",
|
|
68
68
|
"description": "The declared name. With `at`, it identifies the workspace."
|
|
69
69
|
},
|
|
70
|
+
"repo": {
|
|
71
|
+
"type": [
|
|
72
|
+
"string",
|
|
73
|
+
"null"
|
|
74
|
+
],
|
|
75
|
+
"description": "Added in contract 1 by #3168. The repository the document came from: the URL of the remote origin of the git repository that holds the workspace, with any user name or password removed, or null when there is no origin or the workspace is outside git. With name, root and the revision, it says which repository's workspace a document describes, so a reader that gathers several repositories' documents, such as an org workspace's repo members, tells them apart."
|
|
76
|
+
},
|
|
70
77
|
"root": {
|
|
71
78
|
"type": "string",
|
|
72
79
|
"description": "The workspace root relative to the git root, with / separators, \".\" for the git root itself. An absolute path outside a git repository."
|
|
@@ -287,6 +294,33 @@
|
|
|
287
294
|
"type": "string",
|
|
288
295
|
"description": "Relative to the workspace root, with / separators; \".\" is the root member."
|
|
289
296
|
},
|
|
297
|
+
"repo": {
|
|
298
|
+
"type": "object",
|
|
299
|
+
"description": "Added in contract 1 by #3168. For a member of kind repo, the other repository's workspace it names: url, the pinned rev and the workspace root inside that repository. Absent for every other kind. A repo member's dir is @repo/<name>, a marker that names no directory of this repository.",
|
|
300
|
+
"required": [
|
|
301
|
+
"url",
|
|
302
|
+
"rev",
|
|
303
|
+
"workspace"
|
|
304
|
+
],
|
|
305
|
+
"properties": {
|
|
306
|
+
"url": {
|
|
307
|
+
"type": "string"
|
|
308
|
+
},
|
|
309
|
+
"rev": {
|
|
310
|
+
"type": "string"
|
|
311
|
+
},
|
|
312
|
+
"workspace": {
|
|
313
|
+
"type": "string"
|
|
314
|
+
},
|
|
315
|
+
"read": {
|
|
316
|
+
"type": [
|
|
317
|
+
"string",
|
|
318
|
+
"null"
|
|
319
|
+
],
|
|
320
|
+
"description": "The repo identity the other repository's own graph document gave (its workspace.repo), or null when it gave none or the read failed."
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
},
|
|
290
324
|
"kind": {
|
|
291
325
|
"type": "string"
|
|
292
326
|
},
|
|
@@ -71,14 +71,26 @@ describe("identity.attribution", () => {
|
|
|
71
71
|
expect(decl({ attribution: "identified", gates: { ship: { class: "operator" }, rollback: {} } }).identity).toEqual({
|
|
72
72
|
attribution: "identified",
|
|
73
73
|
gates: {
|
|
74
|
-
ship: { gate: "ship", class: "operator", pointer: "/identity/gates/ship" },
|
|
75
|
-
rollback: { gate: "rollback", class: null, pointer: "/identity/gates/rollback" },
|
|
74
|
+
ship: { gate: "ship", class: "operator", quorum: null, pointer: "/identity/gates/ship" },
|
|
75
|
+
rollback: { gate: "rollback", class: null, quorum: null, pointer: "/identity/gates/rollback" },
|
|
76
76
|
},
|
|
77
77
|
});
|
|
78
78
|
expect(() => decl({ attribution: "verified" })).toThrow();
|
|
79
79
|
expect(() => decl({ gates: { ship: { by: "x" } } })).toThrow();
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
+
test("a gate entry takes a quorum beside its class (#4324)", () => {
|
|
83
|
+
const rules = [{ targets: ["prod-*"], count: 2, roles: ["platform"] }, { count: 1 }];
|
|
84
|
+
const gates = decl({ gates: { ship: { class: "operator", quorum: { count: 2 } }, deploy: { quorum: rules } } }).identity!.gates;
|
|
85
|
+
expect(gates.ship).toEqual({ gate: "ship", class: "operator", quorum: { count: 2 }, pointer: "/identity/gates/ship" });
|
|
86
|
+
expect(gates.deploy!.quorum).toEqual(rules);
|
|
87
|
+
// The schema refuses a malformed rule, and the reader refuses notAuthor, which an Op gate can't judge.
|
|
88
|
+
expect(() => decl({ gates: { ship: { quorum: { count: 0 } } } })).toThrow();
|
|
89
|
+
expect(() => decl({ gates: { ship: { quorum: [] } } })).toThrow();
|
|
90
|
+
expect(() => decl({ gates: { ship: { quorum: { count: 1, by: "x" } } } })).toThrow();
|
|
91
|
+
expect(() => decl({ gates: { ship: { quorum: { count: 1, notAuthor: true } } } })).toThrow(/identity\.gates\.ship\.quorum\.notAuthor is not read here/);
|
|
92
|
+
});
|
|
93
|
+
|
|
82
94
|
test("identified refuses a bare name and takes a forge identity or a signer", () => {
|
|
83
95
|
const source = { declaration: decl({ attribution: "identified" }), policy: policyWith([{ principal: "dana@example.test", key: "ssh-ed25519 AAAA" }]) };
|
|
84
96
|
expect(() => refuseUnidentified(source, ["github:alice", "dana@example.test"], "--by")).not.toThrow();
|
package/src/workspace/intent.ts
CHANGED
|
@@ -66,6 +66,7 @@ import { isGeneratedPath } from "./generated-files";
|
|
|
66
66
|
import { entityDecisions, hasTrailer, readCommitJoins, runCommitJoins, type CommitJoins, type IntentCommit, type JoinedEntity, type PluginFinding } from "./intent-joins";
|
|
67
67
|
import { loadKindRegistry } from "./kinds";
|
|
68
68
|
import { resolveLinks, type LinkTableRow } from "./links";
|
|
69
|
+
import { readKindLinks } from "./kind-link-readers";
|
|
69
70
|
import { sourceMemberHandles } from "./member-handles";
|
|
70
71
|
import { constraintCovers, isWorkspacePath, memberHolding } from "./record-assets";
|
|
71
72
|
import type { DecidedIn } from "./record-decided";
|
|
@@ -78,6 +79,7 @@ import { memberDirOnDisk, memberSymbolResolvers, type SymbolPluginLoader } from
|
|
|
78
79
|
import { resolveSymbol, symbolResolverFor, type SymbolResolver } from "./symbols";
|
|
79
80
|
import { readTrailerJoins, type CommitTrailerJoins } from "./trailer-joins";
|
|
80
81
|
import { buildTrace, type IntentTrace } from "./intent-trace";
|
|
82
|
+
import { compareDecisionRank, type WhyRelevance } from "./decision-rank";
|
|
81
83
|
import { joinPath, skippedDir, workingTree, type WorkspaceTree } from "./tree";
|
|
82
84
|
import { activeAttestors, type ProvenanceLevel } from "./trust/attestor";
|
|
83
85
|
import { commitProvenance, policyAtBase, resolveBase } from "./trust/provenance";
|
|
@@ -524,8 +526,7 @@ export interface WhySpan {
|
|
|
524
526
|
via?: string[];
|
|
525
527
|
}
|
|
526
528
|
|
|
527
|
-
|
|
528
|
-
export type WhyRelevance = "carried" | "path" | "contract" | "issue" | "member" | "related";
|
|
529
|
+
export type { WhyRelevance };
|
|
529
530
|
|
|
530
531
|
export interface WhyDecision {
|
|
531
532
|
decision: string;
|
|
@@ -1854,7 +1855,7 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
|
|
|
1854
1855
|
atRevision: located.at !== null,
|
|
1855
1856
|
...(query.loadLexicon ? { load: query.loadLexicon as unknown as import("./lexicon-outputs").OutputsPluginLoader } : {}),
|
|
1856
1857
|
});
|
|
1857
|
-
rows = resolveLinks(declaration, sourceMemberHandles(declaration, located.tree, groups, kindsRegistry, lexiconOutputs)).filter((r) => r.origin === "declared");
|
|
1858
|
+
rows = resolveLinks(declaration, sourceMemberHandles(declaration, located.tree, groups, kindsRegistry, lexiconOutputs), undefined, readKindLinks(declaration, located.tree, kindsRegistry)).filter((r) => r.origin === "declared");
|
|
1858
1859
|
} catch (err) {
|
|
1859
1860
|
if (!(err instanceof WorkspaceReadError)) throw err;
|
|
1860
1861
|
}
|
|
@@ -2080,7 +2081,6 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
|
|
|
2080
2081
|
// The decisions, most relevant first.
|
|
2081
2082
|
const decisionNodes = [...nodes.values()].filter((n): n is DecisionNode => n.kind === "decision");
|
|
2082
2083
|
const ownLines = (d: DecisionNode) => who.filter((w) => w.sha !== null && ownWork(joined.get(w.sha) ?? { contracts: [], authorship: [], decisions: [], carries: [] }, d)).length;
|
|
2083
|
-
const ORDER: WhyRelevance[] = ["carried", "path", "contract", "issue", "member", "related"];
|
|
2084
2084
|
const ranked = decisionNodes.map((d) => {
|
|
2085
2085
|
const lines = ownLines(d);
|
|
2086
2086
|
const carried = lines > 0 || (fileLines === null && touched.some((t) => ownWork(joined.get(t.sha) ?? { contracts: [], authorship: [], decisions: [], carries: [] }, d)));
|
|
@@ -2090,16 +2090,8 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
|
|
|
2090
2090
|
const specificity = Math.max(0, ...d.constrains.filter((c) => c.granularity === "path").map((c) => c.entry.length));
|
|
2091
2091
|
return { d, entry: { decision: d.id, relevance, current: d.supersededBy === null, closed: d.closed, lines } satisfies WhyDecision, specificity };
|
|
2092
2092
|
});
|
|
2093
|
-
ranked.
|
|
2094
|
-
|
|
2095
|
-
Number(b.entry.current) - Number(a.entry.current) ||
|
|
2096
|
-
ORDER.indexOf(a.entry.relevance) - ORDER.indexOf(b.entry.relevance) ||
|
|
2097
|
-
b.entry.lines - a.entry.lines ||
|
|
2098
|
-
Number(b.entry.closed) - Number(a.entry.closed) ||
|
|
2099
|
-
b.specificity - a.specificity ||
|
|
2100
|
-
(b.d.decidedIn?.date ?? "").localeCompare(a.d.decidedIn?.date ?? "") ||
|
|
2101
|
-
a.d.id.localeCompare(b.d.id),
|
|
2102
|
-
);
|
|
2093
|
+
const key = (r: (typeof ranked)[number]) => ({ ...r.entry, id: r.d.id, specificity: r.specificity, decidedOn: r.d.decidedIn?.date ?? null });
|
|
2094
|
+
ranked.sort((a, b) => compareDecisionRank(key(a), key(b)));
|
|
2103
2095
|
const decisions = ranked.map((r) => r.entry);
|
|
2104
2096
|
|
|
2105
2097
|
// The runs that wrote the current lines, most lines first; for a directory, every run in the walk.
|