@intentius/chant 0.124.46 → 0.124.49
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 +41 -14
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +6 -1
- package/dist/cli/handlers/graph-reader.d.ts +26 -0
- package/dist/cli/handlers/graph-reader.d.ts.map +1 -0
- package/dist/cli/handlers/graph-reader.js +41 -0
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +14 -0
- 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 +183 -40
- 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 +87 -14
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +47 -2
- package/dist/cli/registry.d.ts +22 -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/pr-run.d.ts +3 -0
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +1 -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 +25 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -2
- package/dist/gate-decision.d.ts +20 -2
- package/dist/gate-decision.d.ts.map +1 -1
- package/dist/gate-decision.js +30 -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 +94 -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 +9 -6
- 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-base.d.ts +25 -0
- package/dist/op/gate-approval-base.d.ts.map +1 -0
- package/dist/op/gate-approval-base.js +71 -0
- package/dist/op/gate-approval.d.ts +18 -9
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-approval.js +13 -31
- package/dist/op/gate-name.d.ts +13 -0
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-name.js +14 -1
- 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 +47 -5
- 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 +45 -3
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +68 -7
- 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-loop.d.ts +10 -0
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +4 -0
- package/dist/pr-migrations.d.ts +62 -0
- package/dist/pr-migrations.d.ts.map +1 -0
- package/dist/pr-migrations.js +50 -0
- package/dist/pr-post.d.ts +42 -0
- package/dist/pr-post.d.ts.map +1 -1
- package/dist/pr-post.js +68 -2
- 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 +264 -254
- package/dist/workspace/check.schema.json +13 -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/gate-approval.d.ts +58 -0
- package/dist/workspace/checks/gate-approval.d.ts.map +1 -0
- package/dist/workspace/checks/gate-approval.js +103 -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 +12 -1
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +63 -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 +50 -2
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +57 -11
- package/dist/workspace/declaration.schema.json +166 -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/exit-cli.d.ts +44 -0
- package/dist/workspace/exit-cli.d.ts.map +1 -0
- package/dist/workspace/exit-cli.js +198 -0
- package/dist/workspace/exit.d.ts +159 -0
- package/dist/workspace/exit.d.ts.map +1 -0
- package/dist/workspace/exit.js +480 -0
- package/dist/workspace/exit.schema.json +257 -0
- 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-cache.d.ts +3 -1
- package/dist/workspace/graph-cache.d.ts.map +1 -1
- package/dist/workspace/graph-cache.js +12 -1
- 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/import-fetch.d.ts +38 -0
- package/dist/workspace/import-fetch.d.ts.map +1 -0
- package/dist/workspace/import-fetch.js +99 -0
- package/dist/workspace/import-init.d.ts +26 -0
- package/dist/workspace/import-init.d.ts.map +1 -0
- package/dist/workspace/import-init.js +130 -0
- package/dist/workspace/import-source.d.ts +147 -0
- package/dist/workspace/import-source.d.ts.map +1 -0
- package/dist/workspace/import-source.js +370 -0
- package/dist/workspace/init.d.ts +4 -0
- package/dist/workspace/init.d.ts.map +1 -1
- package/dist/workspace/init.js +70 -3
- 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/kind-readers.d.ts +6 -0
- package/dist/workspace/kind-readers.d.ts.map +1 -1
- package/dist/workspace/kind-readers.js +18 -0
- package/dist/workspace/kinds.d.ts +90 -3
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/kinds.js +161 -7
- 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/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.js +13 -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/new-migrations.d.ts +47 -0
- package/dist/workspace/new-migrations.d.ts.map +1 -0
- package/dist/workspace/new-migrations.js +91 -0
- package/dist/workspace/pr-report.schema.json +46 -0
- package/dist/workspace/promote.d.ts +96 -0
- package/dist/workspace/promote.d.ts.map +1 -0
- package/dist/workspace/promote.js +263 -0
- package/dist/workspace/reason-codes.d.ts +21 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +25 -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 +108 -10
- 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 +33 -4
- package/src/cli/handlers/build.ts +7 -1
- package/src/cli/handlers/graph-reader.test.ts +31 -0
- package/src/cli/handlers/graph-reader.ts +51 -0
- package/src/cli/handlers/graph.ts +14 -0
- 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 +176 -37
- 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 +17 -0
- package/src/cli/main.ts +87 -14
- package/src/cli/parse-args.ts +35 -2
- package/src/cli/registry.ts +22 -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-env-run-bundle.test.ts +1 -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/pr-run.ts +4 -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 +24 -1
- package/src/gate-decision-bundle.test.ts +5 -2
- package/src/gate-decision-identity.test.ts +20 -0
- package/src/gate-decision.ts +46 -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 +98 -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-at-base.test.ts +97 -0
- package/src/op/gate-approval-base.ts +72 -0
- package/src/op/gate-approval.test.ts +6 -1
- package/src/op/gate-approval.ts +22 -27
- package/src/op/gate-name.ts +16 -1
- 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 +69 -9
- package/src/op/index.ts +3 -2
- package/src/op/local-executor.ts +91 -11
- 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-loop-bundle.test.ts +3 -2
- package/src/pr-loop.test.ts +52 -0
- package/src/pr-loop.ts +12 -0
- package/src/pr-migrations.ts +97 -0
- package/src/pr-post-open.e2e.test.ts +36 -0
- package/src/pr-post.test.ts +102 -3
- package/src/pr-post.ts +90 -2
- 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/__fixtures__/import/spacelift.json +9 -0
- package/src/workspace/__fixtures__/import/tfc.json +13 -0
- package/src/workspace/behold-kinds.test.ts +2 -1
- package/src/workspace/check-contract.test.ts +8 -6
- package/src/workspace/check.schema.json +13 -2
- package/src/workspace/checks/ci-branch.test.ts +128 -0
- package/src/workspace/checks/ci-branch.ts +141 -0
- package/src/workspace/checks/gate-approval.test.ts +77 -0
- package/src/workspace/checks/gate-approval.ts +121 -0
- package/src/workspace/checks/links.ts +12 -1
- package/src/workspace/checks/org.ts +169 -0
- package/src/workspace/checks.ts +68 -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 +166 -24
- package/src/workspace/declaration.ts +101 -15
- package/src/workspace/environments.ts +40 -4
- package/src/workspace/exit-cli.ts +219 -0
- package/src/workspace/exit.schema.json +257 -0
- package/src/workspace/exit.test.ts +185 -0
- package/src/workspace/exit.ts +560 -0
- package/src/workspace/flyway-dir.ts +113 -0
- package/src/workspace/flyway-layout.ts +337 -0
- package/src/workspace/graph-cache.ts +12 -2
- 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 +29 -2
- package/src/workspace/import-fetch.ts +151 -0
- package/src/workspace/import-init.ts +147 -0
- package/src/workspace/import-source.test.ts +183 -0
- package/src/workspace/import-source.ts +462 -0
- package/src/workspace/init.test.ts +55 -1
- package/src/workspace/init.ts +66 -4
- package/src/workspace/intent.ts +6 -14
- package/src/workspace/kind-link-readers.ts +68 -3
- package/src/workspace/kind-readers.test.ts +36 -0
- package/src/workspace/kind-readers.ts +18 -1
- package/src/workspace/kinds.test.ts +35 -7
- package/src/workspace/kinds.ts +212 -11
- 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/lineage-upgrade.test.ts +20 -0
- package/src/workspace/lineage-upgrade.ts +16 -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/new-migrations.test.ts +98 -0
- package/src/workspace/new-migrations.ts +109 -0
- package/src/workspace/org-scope.test.ts +238 -0
- package/src/workspace/pr-report.schema.json +46 -0
- package/src/workspace/promote.test.ts +114 -0
- package/src/workspace/promote.ts +302 -0
- package/src/workspace/reason-codes.test.ts +10 -1
- package/src/workspace/reason-codes.ts +25 -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 +108 -10
- 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,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant workspace init --from tfc|spacelift|backstage` (#3178): propose a
|
|
3
|
+
* declaration from structure that lives outside the repository.
|
|
4
|
+
*
|
|
5
|
+
* Each source is read into a small input document (see the `*Input` types),
|
|
6
|
+
* and the mapping from that document to a proposal is a pure function. The
|
|
7
|
+
* Terraform Cloud and Spacelift documents come from their APIs
|
|
8
|
+
* (`import-fetch.ts`) or from an exported file given with `--input`; tests
|
|
9
|
+
* feed fixture JSON and never open a connection. Backstage is read from
|
|
10
|
+
* `catalog-info.yaml` files on disk.
|
|
11
|
+
*
|
|
12
|
+
* What maps:
|
|
13
|
+
*
|
|
14
|
+
* - a TFC workspace or a Spacelift stack becomes a member (`terraform` for a
|
|
15
|
+
* Terraform stack, `other` with a `because` for anything else);
|
|
16
|
+
* - a TFC run trigger, or a Spacelift stack dependency, becomes a link on the
|
|
17
|
+
* consumer to an output of the producer;
|
|
18
|
+
* - an environment is read from the `env:` or `environment:` tag or label, or
|
|
19
|
+
* the Backstage `spec.lifecycle`;
|
|
20
|
+
* - policy sets and policies become constraint records;
|
|
21
|
+
* - a Backstage component or resource becomes an `other` member, and its
|
|
22
|
+
* `dependsOn` becomes links.
|
|
23
|
+
*
|
|
24
|
+
* Everything else is listed in `leftOut`, with the reason, so the user sees
|
|
25
|
+
* what the import did not carry.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
29
|
+
import { dirname, join, relative } from "node:path";
|
|
30
|
+
import yaml from "js-yaml";
|
|
31
|
+
import { NAME_PATTERN, parseDeclaration } from "./declaration";
|
|
32
|
+
import { sanitizeName } from "./init";
|
|
33
|
+
|
|
34
|
+
export const IMPORT_SOURCES = ["tfc", "spacelift", "backstage"] as const;
|
|
35
|
+
export type ImportSource = (typeof IMPORT_SOURCES)[number];
|
|
36
|
+
|
|
37
|
+
/** The output every imported producer exposes when its source names none: "this member was applied". */
|
|
38
|
+
export const APPLIED_OUTPUT = "applied";
|
|
39
|
+
/** The output a Backstage entity exposes to the entities that depend on it. */
|
|
40
|
+
export const CATALOG_OUTPUT = "catalog";
|
|
41
|
+
|
|
42
|
+
// ── Inputs ───────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
export interface TfcInput {
|
|
45
|
+
organization: string;
|
|
46
|
+
projects?: { id: string; name: string }[];
|
|
47
|
+
workspaces: {
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
project?: string;
|
|
51
|
+
workingDirectory?: string;
|
|
52
|
+
terraformVersion?: string;
|
|
53
|
+
vcsRepo?: string;
|
|
54
|
+
tags?: string[];
|
|
55
|
+
variables?: { key: string; category?: string; sensitive?: boolean }[];
|
|
56
|
+
/** Workspace ids (or names) whose runs trigger this one. */
|
|
57
|
+
runTriggers?: string[];
|
|
58
|
+
notifications?: number;
|
|
59
|
+
}[];
|
|
60
|
+
policySets?: { id?: string; name: string; kind?: string; enforcement?: string; workspaces?: string[]; global?: boolean }[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface SpaceliftInput {
|
|
64
|
+
account: string;
|
|
65
|
+
stacks: {
|
|
66
|
+
id: string;
|
|
67
|
+
name: string;
|
|
68
|
+
space?: string;
|
|
69
|
+
repository?: string;
|
|
70
|
+
branch?: string;
|
|
71
|
+
projectRoot?: string;
|
|
72
|
+
vendor?: string;
|
|
73
|
+
labels?: string[];
|
|
74
|
+
dependsOn?: { stack: string; references?: { outputName: string; inputName?: string }[] }[];
|
|
75
|
+
policies?: string[];
|
|
76
|
+
contexts?: string[];
|
|
77
|
+
}[];
|
|
78
|
+
policies?: { id: string; name: string; type?: string }[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── Result ───────────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
export interface ImportedMember {
|
|
84
|
+
name: string;
|
|
85
|
+
dir: string;
|
|
86
|
+
kind: string;
|
|
87
|
+
because?: string;
|
|
88
|
+
outputs?: string[];
|
|
89
|
+
links?: { member: string; output: string }[];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface ConstraintRecord {
|
|
93
|
+
id: string;
|
|
94
|
+
kind: "constraint";
|
|
95
|
+
source: ImportSource;
|
|
96
|
+
title: string;
|
|
97
|
+
policyKind?: string;
|
|
98
|
+
enforcement?: string;
|
|
99
|
+
/** The members the constraint applies to; empty means all of them. */
|
|
100
|
+
appliesTo: string[];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface EnvironmentRecord {
|
|
104
|
+
name: string;
|
|
105
|
+
members: string[];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface LeftOut {
|
|
109
|
+
what: string;
|
|
110
|
+
count: number;
|
|
111
|
+
why: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface ImportProposal {
|
|
115
|
+
source: ImportSource;
|
|
116
|
+
declaration: { name: string; schema: 1; members: ImportedMember[] };
|
|
117
|
+
environments: EnvironmentRecord[];
|
|
118
|
+
records: ConstraintRecord[];
|
|
119
|
+
leftOut: LeftOut[];
|
|
120
|
+
/** Plain-language notes the user should read before writing, such as the kind pin the proposal needs. */
|
|
121
|
+
notes: string[];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
function nameFor(raw: string, taken: Set<string>): string {
|
|
127
|
+
let base = sanitizeName(raw) || "member";
|
|
128
|
+
if (!NAME_PATTERN.test(base)) base = "member";
|
|
129
|
+
let name = base;
|
|
130
|
+
for (let i = 2; taken.has(name); i++) name = `${base.slice(0, 36)}-${i}`;
|
|
131
|
+
taken.add(name);
|
|
132
|
+
return name;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function cleanDir(raw: string | undefined, fallback: string): string {
|
|
136
|
+
const d = (raw ?? "").trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
|
|
137
|
+
if (d === "" || d === "." || d.startsWith("/") || d.split("/").includes("..")) return fallback;
|
|
138
|
+
return d;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const ENV_TAG = /^(?:env|environment)[:=](.+)$/i;
|
|
142
|
+
function envOf(tags: string[] | undefined): string[] {
|
|
143
|
+
return (tags ?? []).map((t) => ENV_TAG.exec(t)?.[1]?.trim()).filter((e): e is string => !!e);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function addEnv(map: Map<string, string[]>, env: string, member: string): void {
|
|
147
|
+
const list = map.get(env) ?? [];
|
|
148
|
+
if (!list.includes(member)) list.push(member);
|
|
149
|
+
map.set(env, list);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function envList(map: Map<string, string[]>): EnvironmentRecord[] {
|
|
153
|
+
return [...map].map(([name, members]) => ({ name, members })).sort((a, b) => a.name.localeCompare(b.name));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function addLink(member: ImportedMember, producer: ImportedMember, output: string): void {
|
|
157
|
+
producer.outputs = [...new Set([...(producer.outputs ?? []), output])];
|
|
158
|
+
member.links = [...(member.links ?? [])];
|
|
159
|
+
if (!member.links.some((l) => l.member === producer.name && l.output === output)) member.links.push({ member: producer.name, output });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function finish(source: ImportSource, workspaceName: string, members: ImportedMember[], parts: Omit<ImportProposal, "source" | "declaration">): ImportProposal {
|
|
163
|
+
const declaration = { name: nameFor(workspaceName, new Set()), schema: 1 as const, members };
|
|
164
|
+
// The same check `init` makes: it must read back as a valid declaration.
|
|
165
|
+
parseDeclaration(JSON.stringify(declaration), "chant.workspace.json");
|
|
166
|
+
return { source, declaration, ...parts };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const TF_NOTE =
|
|
170
|
+
"Members of kind terraform get their kind from @intentius/chant-lexicon-terraform: pin that package in the declaration's pins before chant workspace check will read them.";
|
|
171
|
+
|
|
172
|
+
// ── Terraform Cloud ──────────────────────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
export function proposeFromTfc(input: TfcInput, options: { name?: string } = {}): ImportProposal {
|
|
175
|
+
const taken = new Set<string>();
|
|
176
|
+
const byRef = new Map<string, ImportedMember>();
|
|
177
|
+
const members: ImportedMember[] = [];
|
|
178
|
+
const projects = new Map((input.projects ?? []).map((p) => [p.id, p.name]));
|
|
179
|
+
const envs = new Map<string, string[]>();
|
|
180
|
+
let variables = 0;
|
|
181
|
+
let sensitive = 0;
|
|
182
|
+
let vcs = 0;
|
|
183
|
+
let notifications = 0;
|
|
184
|
+
|
|
185
|
+
const byDir = new Map<string, ImportedMember>();
|
|
186
|
+
let shared = 0;
|
|
187
|
+
for (const w of input.workspaces) {
|
|
188
|
+
const dirKey = cleanDir(w.workingDirectory, `\0${w.name}`);
|
|
189
|
+
const first = byDir.get(dirKey);
|
|
190
|
+
if (first) {
|
|
191
|
+
// One member per directory: a workspace on the same configuration folds into the first.
|
|
192
|
+
byRef.set(w.id, first);
|
|
193
|
+
byRef.set(w.name, first);
|
|
194
|
+
for (const e of envOf(w.tags)) addEnv(envs, e, first.name);
|
|
195
|
+
shared++;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const name = nameFor(w.name, taken);
|
|
199
|
+
const project = w.project ? projects.get(w.project) : undefined;
|
|
200
|
+
const m: ImportedMember = {
|
|
201
|
+
name,
|
|
202
|
+
dir: dirKey.startsWith("\0") ? name : dirKey,
|
|
203
|
+
kind: "terraform",
|
|
204
|
+
because: `Terraform Cloud workspace ${input.organization}/${w.name}${project ? ` in project ${project}` : ""}`,
|
|
205
|
+
};
|
|
206
|
+
members.push(m);
|
|
207
|
+
byDir.set(dirKey, m);
|
|
208
|
+
byRef.set(w.id, m);
|
|
209
|
+
byRef.set(w.name, m);
|
|
210
|
+
for (const e of envOf(w.tags)) addEnv(envs, e, name);
|
|
211
|
+
variables += w.variables?.length ?? 0;
|
|
212
|
+
sensitive += (w.variables ?? []).filter((v) => v.sensitive).length;
|
|
213
|
+
if (w.vcsRepo) vcs++;
|
|
214
|
+
notifications += w.notifications ?? 0;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let triggers = 0;
|
|
218
|
+
let dangling = 0;
|
|
219
|
+
for (const w of input.workspaces) {
|
|
220
|
+
const consumer = byRef.get(w.id)!;
|
|
221
|
+
for (const src of w.runTriggers ?? []) {
|
|
222
|
+
const producer = byRef.get(src);
|
|
223
|
+
if (!producer || producer === consumer) {
|
|
224
|
+
dangling++;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
addLink(consumer, producer, APPLIED_OUTPUT);
|
|
228
|
+
triggers++;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const records: ConstraintRecord[] = (input.policySets ?? []).map((p, i) => ({
|
|
233
|
+
id: `tfc-policy-${sanitizeName(p.name) || i}`,
|
|
234
|
+
kind: "constraint",
|
|
235
|
+
source: "tfc",
|
|
236
|
+
title: p.name,
|
|
237
|
+
...(p.kind ? { policyKind: p.kind } : {}),
|
|
238
|
+
...(p.enforcement ? { enforcement: p.enforcement } : {}),
|
|
239
|
+
appliesTo: p.global ? [] : (p.workspaces ?? []).map((r) => byRef.get(r)?.name).filter((n): n is string => !!n),
|
|
240
|
+
}));
|
|
241
|
+
|
|
242
|
+
const leftOut: LeftOut[] = [];
|
|
243
|
+
if (variables > 0) leftOut.push({ what: "variables", count: variables, why: `${sensitive} are sensitive and their values are never read; the keys stay in Terraform Cloud` });
|
|
244
|
+
if (vcs > 0) leftOut.push({ what: "VCS connections", count: vcs, why: "the repository link belongs to the CI that replaces Terraform Cloud's runs" });
|
|
245
|
+
if (notifications > 0) leftOut.push({ what: "notification configurations", count: notifications, why: "a workspace has no notification record" });
|
|
246
|
+
if (shared > 0) leftOut.push({ what: "workspaces sharing a directory", count: shared, why: "a member is one directory; each folds into the first workspace on it, with its environments and run triggers" });
|
|
247
|
+
if (dangling > 0) leftOut.push({ what: "run triggers", count: dangling, why: "their source workspace is not in the input, or is the workspace itself" });
|
|
248
|
+
if ((input.projects ?? []).length > 0) leftOut.push({ what: "projects", count: input.projects!.length, why: "named in each member's because; a project is not a declared grouping" });
|
|
249
|
+
leftOut.push({ what: "state and run history", count: input.workspaces.length, why: "state is adopted when a member is promoted, not copied by init" });
|
|
250
|
+
void triggers;
|
|
251
|
+
|
|
252
|
+
return finish("tfc", options.name ?? input.organization, members, { environments: envList(envs), records, leftOut, notes: [TF_NOTE] });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ── Spacelift ────────────────────────────────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
export function proposeFromSpacelift(input: SpaceliftInput, options: { name?: string } = {}): ImportProposal {
|
|
258
|
+
const taken = new Set<string>();
|
|
259
|
+
const byRef = new Map<string, ImportedMember>();
|
|
260
|
+
const members: ImportedMember[] = [];
|
|
261
|
+
const envs = new Map<string, string[]>();
|
|
262
|
+
let nonTerraform = 0;
|
|
263
|
+
let contexts = 0;
|
|
264
|
+
|
|
265
|
+
const byDir = new Map<string, ImportedMember>();
|
|
266
|
+
let shared = 0;
|
|
267
|
+
for (const s of input.stacks) {
|
|
268
|
+
const dirKey = cleanDir(s.projectRoot, `\0${s.name}`);
|
|
269
|
+
const first = byDir.get(dirKey);
|
|
270
|
+
if (first) {
|
|
271
|
+
byRef.set(s.id, first);
|
|
272
|
+
byRef.set(s.name, first);
|
|
273
|
+
for (const e of envOf(s.labels)) addEnv(envs, e, first.name);
|
|
274
|
+
shared++;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const name = nameFor(s.name, taken);
|
|
278
|
+
const vendor = (s.vendor ?? "terraform").toLowerCase();
|
|
279
|
+
const tf = vendor === "terraform" || vendor === "opentofu";
|
|
280
|
+
if (!tf) nonTerraform++;
|
|
281
|
+
const m: ImportedMember = {
|
|
282
|
+
name,
|
|
283
|
+
dir: dirKey.startsWith("\0") ? name : dirKey,
|
|
284
|
+
kind: tf ? "terraform" : "other",
|
|
285
|
+
because: `Spacelift stack ${s.id}${s.space ? ` in space ${s.space}` : ""}${tf ? "" : `, ${vendor}`}`,
|
|
286
|
+
};
|
|
287
|
+
members.push(m);
|
|
288
|
+
byDir.set(dirKey, m);
|
|
289
|
+
byRef.set(s.id, m);
|
|
290
|
+
byRef.set(s.name, m);
|
|
291
|
+
for (const e of envOf(s.labels)) addEnv(envs, e, name);
|
|
292
|
+
contexts += s.contexts?.length ?? 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
let dangling = 0;
|
|
296
|
+
for (const s of input.stacks) {
|
|
297
|
+
const consumer = byRef.get(s.id)!;
|
|
298
|
+
for (const dep of s.dependsOn ?? []) {
|
|
299
|
+
const producer = byRef.get(dep.stack);
|
|
300
|
+
if (!producer || producer === consumer) {
|
|
301
|
+
dangling++;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
const outs = (dep.references ?? []).map((r) => r.outputName);
|
|
305
|
+
for (const o of outs.length > 0 ? outs : [APPLIED_OUTPUT]) addLink(consumer, producer, o);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const attached = new Map<string, string[]>();
|
|
310
|
+
for (const s of input.stacks) for (const p of s.policies ?? []) attached.set(p, [...(attached.get(p) ?? []), byRef.get(s.id)!.name]);
|
|
311
|
+
const records: ConstraintRecord[] = (input.policies ?? []).map((p) => ({
|
|
312
|
+
id: `spacelift-policy-${sanitizeName(p.name) || sanitizeName(p.id)}`,
|
|
313
|
+
kind: "constraint",
|
|
314
|
+
source: "spacelift",
|
|
315
|
+
title: p.name,
|
|
316
|
+
...(p.type ? { policyKind: p.type } : {}),
|
|
317
|
+
appliesTo: attached.get(p.id) ?? [],
|
|
318
|
+
}));
|
|
319
|
+
|
|
320
|
+
const leftOut: LeftOut[] = [];
|
|
321
|
+
if (contexts > 0) leftOut.push({ what: "context attachments", count: contexts, why: "contexts hold variables and mounted files, which are never read" });
|
|
322
|
+
if (shared > 0) leftOut.push({ what: "stacks sharing a project root", count: shared, why: "a member is one directory; each folds into the first stack on it" });
|
|
323
|
+
if (dangling > 0) leftOut.push({ what: "stack dependencies", count: dangling, why: "the dependency's stack is not in the input, or is the stack itself" });
|
|
324
|
+
if (nonTerraform > 0) leftOut.push({ what: "non-Terraform stacks kept as other", count: nonTerraform, why: "only Terraform and OpenTofu stacks take the terraform kind" });
|
|
325
|
+
leftOut.push({ what: "worker pools, cloud integrations and run history", count: input.stacks.length, why: "not structure of the workspace" });
|
|
326
|
+
|
|
327
|
+
return finish("spacelift", options.name ?? input.account, members, { environments: envList(envs), records, leftOut, notes: [TF_NOTE] });
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ── Backstage ────────────────────────────────────────────────────────────────
|
|
331
|
+
|
|
332
|
+
interface Entity {
|
|
333
|
+
apiVersion?: string;
|
|
334
|
+
kind?: string;
|
|
335
|
+
metadata?: { name?: string; namespace?: string };
|
|
336
|
+
spec?: { lifecycle?: string; dependsOn?: unknown; consumesApis?: unknown; providesApis?: unknown; owner?: string; system?: string };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", ".chant"]);
|
|
340
|
+
|
|
341
|
+
/** Every `catalog-info.yaml` under `root`, relative and `/`-separated. */
|
|
342
|
+
export function findCatalogFiles(root: string): string[] {
|
|
343
|
+
const out: string[] = [];
|
|
344
|
+
const walk = (dir: string): void => {
|
|
345
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
346
|
+
if (e.isDirectory()) {
|
|
347
|
+
if (!SKIP_DIRS.has(e.name)) walk(join(dir, e.name));
|
|
348
|
+
} else if (e.isFile() && /^catalog-info\.ya?ml$/.test(e.name)) {
|
|
349
|
+
out.push(relative(root, join(dir, e.name)).split("\\").join("/"));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
if (existsSync(root)) walk(root);
|
|
354
|
+
return out.sort();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const refKey = (kind: string, ns: string | undefined, name: string): string => `${kind.toLowerCase()}:${ns ?? "default"}/${name}`;
|
|
358
|
+
|
|
359
|
+
function asRefs(v: unknown): string[] {
|
|
360
|
+
return Array.isArray(v) ? v.filter((x): x is string => typeof x === "string") : [];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Normalize `name`, `kind:name` and `kind:ns/name` to `kind:ns/name`. */
|
|
364
|
+
function normalizeRef(ref: string, defaultKind: string): string {
|
|
365
|
+
const m = /^(?:([A-Za-z]+):)?(?:([^/]+)\/)?(.+)$/.exec(ref.trim());
|
|
366
|
+
if (!m) return ref;
|
|
367
|
+
return refKey(m[1] ?? defaultKind, m[2], m[3]);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export function proposeFromBackstage(root: string, options: { name?: string } = {}): ImportProposal {
|
|
371
|
+
const files = findCatalogFiles(root);
|
|
372
|
+
const taken = new Set<string>();
|
|
373
|
+
const byRef = new Map<string, ImportedMember>();
|
|
374
|
+
const byDir = new Map<string, ImportedMember>();
|
|
375
|
+
const members: ImportedMember[] = [];
|
|
376
|
+
const entities: { entity: Entity; member?: ImportedMember; ref: string }[] = [];
|
|
377
|
+
const envs = new Map<string, string[]>();
|
|
378
|
+
const skipped = new Map<string, number>();
|
|
379
|
+
const bump = (k: string): void => void skipped.set(k, (skipped.get(k) ?? 0) + 1);
|
|
380
|
+
|
|
381
|
+
for (const file of files) {
|
|
382
|
+
let docs: unknown[];
|
|
383
|
+
try {
|
|
384
|
+
docs = yaml.loadAll(readFileSync(join(root, file), "utf-8"));
|
|
385
|
+
} catch {
|
|
386
|
+
bump("unreadable catalog-info.yaml");
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const dir = cleanDir(dirname(file), "");
|
|
390
|
+
for (const doc of docs) {
|
|
391
|
+
const entity = doc as Entity;
|
|
392
|
+
const kind = entity?.kind;
|
|
393
|
+
const name = entity?.metadata?.name;
|
|
394
|
+
if (typeof kind !== "string" || typeof name !== "string") continue;
|
|
395
|
+
const ref = refKey(kind, entity.metadata?.namespace, name);
|
|
396
|
+
if (kind !== "Component" && kind !== "Resource") {
|
|
397
|
+
bump(kind);
|
|
398
|
+
entities.push({ entity, ref });
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
let member = byDir.get(dir);
|
|
402
|
+
if (member) {
|
|
403
|
+
// One member per directory: a second entity in the directory folds into the first.
|
|
404
|
+
byRef.set(ref, member);
|
|
405
|
+
bump(`${kind} sharing a directory with a member`);
|
|
406
|
+
} else {
|
|
407
|
+
member = {
|
|
408
|
+
name: nameFor(name, taken),
|
|
409
|
+
dir: dir === "" ? "." : dir,
|
|
410
|
+
kind: "other",
|
|
411
|
+
because: `Backstage ${kind.toLowerCase()} ${name}${entity.spec?.system ? ` of system ${entity.spec.system}` : ""}`,
|
|
412
|
+
};
|
|
413
|
+
members.push(member);
|
|
414
|
+
byDir.set(dir, member);
|
|
415
|
+
byRef.set(ref, member);
|
|
416
|
+
const lifecycle = entity.spec?.lifecycle;
|
|
417
|
+
if (lifecycle) addEnv(envs, lifecycle, member.name);
|
|
418
|
+
}
|
|
419
|
+
entities.push({ entity, member, ref });
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const apiProviders = new Map<string, ImportedMember>();
|
|
424
|
+
for (const { entity, member } of entities) {
|
|
425
|
+
if (member) for (const api of asRefs(entity.spec?.providesApis)) apiProviders.set(normalizeRef(api, "api"), member);
|
|
426
|
+
}
|
|
427
|
+
let dangling = 0;
|
|
428
|
+
for (const { entity, member } of entities) {
|
|
429
|
+
if (!member) continue;
|
|
430
|
+
const wants: { producer: ImportedMember | undefined }[] = [
|
|
431
|
+
...asRefs(entity.spec?.dependsOn).map((r) => ({ producer: byRef.get(normalizeRef(r, "component")) })),
|
|
432
|
+
...asRefs(entity.spec?.consumesApis).map((r) => ({ producer: apiProviders.get(normalizeRef(r, "api")) })),
|
|
433
|
+
];
|
|
434
|
+
for (const { producer } of wants) {
|
|
435
|
+
if (!producer || producer === member) {
|
|
436
|
+
dangling++;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
addLink(member, producer, CATALOG_OUTPUT);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const leftOut: LeftOut[] = [...skipped].map(([what, count]) => ({
|
|
444
|
+
what,
|
|
445
|
+
count,
|
|
446
|
+
why: what.includes("sharing a directory") ? "a member is one directory; its dependsOn links fold into the first entity there" : "not a unit of code or infrastructure; its owner and system stay in the catalog",
|
|
447
|
+
}));
|
|
448
|
+
if (dangling > 0) leftOut.push({ what: "dependsOn and consumesApis references", count: dangling, why: "they name an entity outside the files read, or the entity itself" });
|
|
449
|
+
if (files.length === 0) leftOut.push({ what: "catalog-info.yaml files", count: 0, why: `none found under ${root}` });
|
|
450
|
+
|
|
451
|
+
const wsName = options.name ?? (root.split(/[\\/]/).filter(Boolean).pop() ?? "backstage");
|
|
452
|
+
return finish("backstage", wsName, members, { environments: envList(envs), records: [], leftOut, notes: [] });
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// ── Reading an input file ────────────────────────────────────────────────────
|
|
456
|
+
|
|
457
|
+
export function readInputFile(source: "tfc" | "spacelift", path: string): TfcInput | SpaceliftInput {
|
|
458
|
+
const raw = JSON.parse(readFileSync(path, "utf-8")) as Record<string, unknown>;
|
|
459
|
+
if (source === "tfc" && !Array.isArray(raw.workspaces)) throw new Error(`${path} has no "workspaces" array: it is not a Terraform Cloud export`);
|
|
460
|
+
if (source === "spacelift" && !Array.isArray(raw.stacks)) throw new Error(`${path} has no "stacks" array: it is not a Spacelift export`);
|
|
461
|
+
return raw as unknown as TfcInput | SpaceliftInput;
|
|
462
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { afterAll, afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
@@ -56,6 +56,60 @@ const ACME = {
|
|
|
56
56
|
"test/unit3/x.ts": "",
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
+
describe("proposeWorkspace with package kinds (#3179)", () => {
|
|
60
|
+
/** Install the real kinds files of the helm and k8s lexicons under node_modules, as a package manager would. */
|
|
61
|
+
function install(root: string): void {
|
|
62
|
+
for (const lexicon of ["helm", "k8s"]) {
|
|
63
|
+
const from = join(REPO, "lexicons", lexicon);
|
|
64
|
+
const to = join(root, "node_modules", "@intentius", `chant-lexicon-${lexicon}`);
|
|
65
|
+
mkdirSync(to, { recursive: true });
|
|
66
|
+
cpSync(join(from, "workspace-kinds.json"), join(to, "workspace-kinds.json"));
|
|
67
|
+
if (existsSync(join(from, "schemas"))) cpSync(join(from, "schemas"), join(to, "schemas"), { recursive: true });
|
|
68
|
+
const { name, version, exports, files } = JSON.parse(readFileSync(join(from, "package.json"), "utf-8"));
|
|
69
|
+
writeFileSync(join(to, "package.json"), JSON.stringify({ name, version, exports, files }));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const files = {
|
|
74
|
+
"charts/web/Chart.yaml": "apiVersion: v2\nname: web\nversion: 0.1.0\n",
|
|
75
|
+
"charts/web/values.yaml": "a: 1\n",
|
|
76
|
+
"deploy/prod/kustomization.yaml": "resources:\n - ../base\n",
|
|
77
|
+
"gitops/guestbook.yaml": "apiVersion: argoproj.io/v1alpha1\nkind: Application\nmetadata:\n name: guestbook\nspec: {}\n",
|
|
78
|
+
"tools/docs/package.json": pkg("docs-site"),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
test("proposes a chart, an overlay and an Argo app as three typed members, with their packages pinned", () => {
|
|
82
|
+
const root = repo(files);
|
|
83
|
+
install(root);
|
|
84
|
+
const { declaration } = proposeWorkspace(root);
|
|
85
|
+
expect(declaration.members.map((m) => [m.dir, m.kind])).toEqual([
|
|
86
|
+
["charts/web", "helm-chart"],
|
|
87
|
+
["deploy/prod", "kustomize"],
|
|
88
|
+
["gitops", "argo-app"],
|
|
89
|
+
["tools/docs", "other"],
|
|
90
|
+
]);
|
|
91
|
+
expect(declaration.pins?.map((p) => p.package)).toEqual(["@intentius/chant-lexicon-helm", "@intentius/chant-lexicon-k8s"]);
|
|
92
|
+
// The proposal is a valid declaration, and the kinds resolve through its pins.
|
|
93
|
+
expect(() => parseDeclaration(JSON.stringify(declaration), "chant.workspace.json")).not.toThrow();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("pins only the packages whose kinds it used, and proposes nothing typed with no kinds installed", () => {
|
|
97
|
+
const root = repo({ "charts/web/Chart.yaml": files["charts/web/Chart.yaml"] });
|
|
98
|
+
install(root);
|
|
99
|
+
expect(proposeWorkspace(root).declaration.pins?.map((p) => p.package)).toEqual(["@intentius/chant-lexicon-helm"]);
|
|
100
|
+
const bare = repo(files);
|
|
101
|
+
const proposal = proposeWorkspace(bare).declaration;
|
|
102
|
+
expect(proposal.pins).toBeUndefined();
|
|
103
|
+
expect(proposal.members.map((m) => m.kind)).toEqual(["other"]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("a typed directory with a package.json is not also proposed as other", () => {
|
|
107
|
+
const root = repo({ "app/Chart.yaml": files["charts/web/Chart.yaml"], "app/package.json": pkg("app") });
|
|
108
|
+
install(root);
|
|
109
|
+
expect(proposeWorkspace(root).declaration.members.map((m) => [m.dir, m.kind])).toEqual([["app", "helm-chart"]]);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
59
113
|
describe("proposeWorkspace (#2534)", () => {
|
|
60
114
|
test("proposes members from projects, nested workspaces and packages, and example groups (ws-051)", () => {
|
|
61
115
|
const root = repo(ACME, "git@github.com:acme/platform.git");
|
package/src/workspace/init.ts
CHANGED
|
@@ -48,10 +48,11 @@ import {
|
|
|
48
48
|
} from "./declaration";
|
|
49
49
|
import { expandGlob } from "./glob";
|
|
50
50
|
import { setProperty } from "./json-edit";
|
|
51
|
-
import {
|
|
51
|
+
import { dataExportTarget } from "./class-data";
|
|
52
|
+
import { holdsChantProject, loadKindRegistry, resolveKind } from "./kinds";
|
|
52
53
|
import { gitTop, skippedDir, workingTree, type WorkspaceTree } from "./tree";
|
|
53
54
|
|
|
54
|
-
const USAGE = "chant workspace init [dir] [--name <name>] [--yes] [--verbose]\n chant workspace init --profile ideation|app|infra [dir] [--name <name>] [--param name=<display name>] [--from <repo>@<ref>|<chant checkout>] [--yes]";
|
|
55
|
+
const USAGE = "chant workspace init [dir] [--name <name>] [--yes] [--verbose]\n chant workspace init --profile ideation|app|infra [dir] [--name <name>] [--param name=<display name>] [--from <repo>@<ref>|<chant checkout>] [--yes]\n chant workspace init --from tfc|spacelift|backstage [dir] [--org <organization>] [--endpoint <url>] [--input <export.json>] [--token <token>] [--yes]";
|
|
55
56
|
|
|
56
57
|
/** The profiles `--profile` takes (#3174), each a directory under {@link PROFILES_DIR} in the chant repository. */
|
|
57
58
|
export const PROFILES = ["ideation", "app", "infra"] as const;
|
|
@@ -119,7 +120,7 @@ export interface StackProposal {
|
|
|
119
120
|
export interface Proposal {
|
|
120
121
|
root: string;
|
|
121
122
|
/** The declaration as it would be written. */
|
|
122
|
-
declaration: { name: string; schema: 1; members: ProposedEntry[] };
|
|
123
|
+
declaration: { name: string; schema: 1; pins?: { package: string; version: string }[]; members: ProposedEntry[] };
|
|
123
124
|
/** Directories leaving the root project, with the .ts source files each holds. */
|
|
124
125
|
leaving: { dir: string; owner: string; files: string[] }[];
|
|
125
126
|
/** One ownership stack per `chant` member, all distinct (ws-037). */
|
|
@@ -384,6 +385,33 @@ function groupName(glob: string): string {
|
|
|
384
385
|
|
|
385
386
|
// ── The proposal ─────────────────────────────────────────────────────────────
|
|
386
387
|
|
|
388
|
+
/**
|
|
389
|
+
* The packages installed at `root` that supply member kinds, as pins at their
|
|
390
|
+
* installed versions. A declaration only reads kinds from its pins, so a
|
|
391
|
+
* proposal that names a package's kind has to pin the package too.
|
|
392
|
+
*/
|
|
393
|
+
function installedKindPins(root: string): { package: string; version: string }[] {
|
|
394
|
+
const scope = join(root, "node_modules", "@intentius");
|
|
395
|
+
let names: string[];
|
|
396
|
+
try {
|
|
397
|
+
names = readdirSync(scope).sort();
|
|
398
|
+
} catch {
|
|
399
|
+
return [];
|
|
400
|
+
}
|
|
401
|
+
const pins: { package: string; version: string }[] = [];
|
|
402
|
+
for (const n of names) {
|
|
403
|
+
try {
|
|
404
|
+
const pkg = JSON.parse(readFileSync(join(scope, n, "package.json"), "utf-8")) as Record<string, unknown>;
|
|
405
|
+
if (typeof pkg.name === "string" && typeof pkg.version === "string" && typeof dataExportTarget(pkg, "./workspace-kinds") === "string") {
|
|
406
|
+
pins.push({ package: pkg.name, version: pkg.version });
|
|
407
|
+
}
|
|
408
|
+
} catch {
|
|
409
|
+
// Not a package, or not readable: it supplies nothing.
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return pins;
|
|
413
|
+
}
|
|
414
|
+
|
|
387
415
|
export function proposeWorkspace(root: string, options: { name?: string } = {}): Proposal {
|
|
388
416
|
const tree = workingTree(root);
|
|
389
417
|
const files = listFiles(root);
|
|
@@ -424,7 +452,26 @@ export function proposeWorkspace(root: string, options: { name?: string } = {}):
|
|
|
424
452
|
[...matches].some((m) => isInside(d, m)) ||
|
|
425
453
|
chantMembers.some((c) => isInside(d, c) || isInside(c, d)) ||
|
|
426
454
|
exampleMatch(d) !== undefined;
|
|
427
|
-
|
|
455
|
+
// A directory that an installed package's kind claims is a typed member
|
|
456
|
+
// (#3179), taking the place of `other` for the same directory. Members
|
|
457
|
+
// don't nest, so a typed member is the outermost one, and never sits inside
|
|
458
|
+
// a project or package that is a member itself.
|
|
459
|
+
const packageDirs = outermost([...pkgDirs].filter((d) => d !== "" && !claimed(d)));
|
|
460
|
+
const kindPins = installedKindPins(root);
|
|
461
|
+
const kindRegistry = kindPins.length > 0 ? loadKindRegistry(kindPins.map((p) => ({ ...p, path: null, integrity: null })), root).registry : undefined;
|
|
462
|
+
const typed = new Map<string, string>();
|
|
463
|
+
if (kindRegistry) {
|
|
464
|
+
const winners = new Map<string, string>();
|
|
465
|
+
const others = [...configDirs, ...nested, ...packageDirs];
|
|
466
|
+
for (const d of new Set(files.map(dirOf))) {
|
|
467
|
+
if (d === "" || claimed(d) || configDirs.has(d) || nested.includes(d)) continue;
|
|
468
|
+
if (others.some((o) => o !== d && isInside(d, o))) continue;
|
|
469
|
+
const winner = resolveKind(kindRegistry, tree, d).winner;
|
|
470
|
+
if (winner && winner.source !== "builtin") winners.set(d, winner.name);
|
|
471
|
+
}
|
|
472
|
+
for (const d of outermost(winners.keys())) typed.set(d, winners.get(d)!);
|
|
473
|
+
}
|
|
474
|
+
const packages = packageDirs.filter((d) => ![...typed.keys()].some((t) => isInside(d, t)));
|
|
428
475
|
|
|
429
476
|
const taken = new Set<string>([name]);
|
|
430
477
|
const entries: (ProposedEntry & { sort: string })[] = [];
|
|
@@ -440,6 +487,9 @@ export function proposeWorkspace(root: string, options: { name?: string } = {}):
|
|
|
440
487
|
because: npmWorkspaces.has(d) ? "an npm workspace package with no chant project" : "an npm package with no chant project",
|
|
441
488
|
});
|
|
442
489
|
}
|
|
490
|
+
for (const [d, kind] of typed) {
|
|
491
|
+
entries.push({ sort: d, name: uniqueName(memberName(tree, d, name), taken), dir: d, kind });
|
|
492
|
+
}
|
|
443
493
|
entries.sort((a, b) => (a.sort < b.sort ? -1 : a.sort > b.sort ? 1 : 0));
|
|
444
494
|
taken.delete(name);
|
|
445
495
|
|
|
@@ -453,9 +503,12 @@ export function proposeWorkspace(root: string, options: { name?: string } = {}):
|
|
|
453
503
|
.map(([n, globs]) => ({ name: uniqueName(n, taken), kind: "examples", glob: globs.length === 1 ? globs[0] : globs }))
|
|
454
504
|
.sort((a, b) => ([a.glob].flat()[0] < [b.glob].flat()[0] ? -1 : 1));
|
|
455
505
|
|
|
506
|
+
const usedSources = new Set([...typed.values()].map((k) => kindRegistry!.get(k)!.source));
|
|
507
|
+
const pins = kindPins.filter((p) => usedSources.has(p.package));
|
|
456
508
|
const declaration = {
|
|
457
509
|
name,
|
|
458
510
|
schema: 1 as const,
|
|
511
|
+
...(pins.length > 0 ? { pins } : {}),
|
|
459
512
|
members: [...entries.map(({ sort: _sort, ...e }) => e), ...groups],
|
|
460
513
|
};
|
|
461
514
|
|
|
@@ -575,6 +628,15 @@ export async function runWorkspaceInit(ctx: CommandContext): Promise<number> {
|
|
|
575
628
|
}
|
|
576
629
|
|
|
577
630
|
if (args.profile !== undefined) return initProfile(args, root);
|
|
631
|
+
// #3178: --from tfc|spacelift|backstage proposes from structure held outside the repository.
|
|
632
|
+
const { isImportSource, runImportInit, IMPORT_USAGE } = await import("./import-init");
|
|
633
|
+
if (args.migrateFrom !== undefined) {
|
|
634
|
+
if (!isImportSource(args.migrateFrom)) {
|
|
635
|
+
console.error(formatError({ message: `--from ${JSON.stringify(args.migrateFrom)} is not a source; the sources are tfc, spacelift, backstage`, hint: IMPORT_USAGE }));
|
|
636
|
+
return 1;
|
|
637
|
+
}
|
|
638
|
+
return runImportInit(args.migrateFrom, root, args);
|
|
639
|
+
}
|
|
578
640
|
|
|
579
641
|
let proposal: Proposal;
|
|
580
642
|
try {
|