@intentius/chant 0.124.31 → 0.124.33
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/bin/chant +7 -2
- package/dist/ci-jobs.d.ts +4 -0
- package/dist/ci-jobs.d.ts.map +1 -1
- package/dist/ci-jobs.js +9 -2
- package/dist/cli/commands/check-lexicon-plugin.d.ts +7 -4
- package/dist/cli/commands/check-lexicon-plugin.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-plugin.js +10 -7
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.js +18 -12
- package/dist/cli/commands/import.d.ts +10 -0
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/import.js +37 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/project.js +4 -3
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/onboard.js +8 -4
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +2 -1
- package/dist/cli/dependencies.d.ts +18 -0
- package/dist/cli/dependencies.d.ts.map +1 -0
- package/dist/cli/dependencies.js +120 -0
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +38 -4
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +12 -4
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +16 -6
- package/dist/cli/handlers/lifecycle-health.d.ts +16 -0
- package/dist/cli/handlers/lifecycle-health.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-health.js +150 -0
- package/dist/cli/handlers/lifecycle.d.ts +26 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.js +1 -1
- package/dist/cli/handlers/lint.d.ts.map +1 -1
- package/dist/cli/handlers/lint.js +6 -0
- package/dist/cli/handlers/operator.d.ts +2 -2
- package/dist/cli/handlers/operator.js +3 -3
- package/dist/cli/handlers/pr.d.ts +16 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +610 -32
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +19 -2
- package/dist/cli/handlers/release.d.ts +16 -1
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +83 -11
- package/dist/cli/handlers/run.d.ts +13 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +38 -15
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +121 -33
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +37 -0
- package/dist/cli/registry.d.ts +29 -6
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/registry.js +6 -2
- package/dist/codegen/generate-post-synth-barrel.js +2 -2
- package/dist/codegen/lexicon-regen.d.ts +4 -0
- package/dist/codegen/lexicon-regen.d.ts.map +1 -1
- package/dist/codegen/lexicon-regen.js +1 -1
- package/dist/codegen/pinned-upgrade.js +2 -2
- package/dist/components/capability.d.ts +44 -3
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/capability.js +21 -0
- package/dist/components/changed-files.d.ts +7 -1
- package/dist/components/changed-files.d.ts.map +1 -1
- package/dist/components/changed-files.js +9 -1
- package/dist/components/deploy-inputs.d.ts +91 -0
- package/dist/components/deploy-inputs.d.ts.map +1 -0
- package/dist/components/deploy-inputs.js +184 -0
- package/dist/components/driver.d.ts +38 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +56 -6
- package/dist/components/fan-out-record.d.ts +6 -1
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-record.js +24 -0
- package/dist/components/fan-out-run.d.ts +20 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +21 -1
- package/dist/components/gate-plan.d.ts +8 -0
- package/dist/components/gate-plan.d.ts.map +1 -1
- package/dist/components/gate-plan.js +6 -0
- package/dist/components/pr-pipeline.d.ts +98 -12
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +146 -23
- package/dist/components/pr-run.d.ts +128 -30
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +290 -70
- package/dist/components/release-compile.d.ts +37 -2
- package/dist/components/release-compile.d.ts.map +1 -1
- package/dist/components/release-compile.js +64 -8
- package/dist/components/release-promote.d.ts +17 -3
- package/dist/components/release-promote.d.ts.map +1 -1
- package/dist/components/release-promote.js +79 -18
- package/dist/components/verbs/bom-writer.d.ts +1 -1
- package/dist/components/verbs/bom-writer.d.ts.map +1 -1
- package/dist/components/verbs/lockfile-sbom-generator.d.ts +20 -0
- package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -1
- package/dist/components/verbs/lockfile-sbom-generator.js +41 -4
- package/dist/components/wave-plan.d.ts +15 -2
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +44 -5
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/fold-import.js +16 -6
- package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
- package/dist/discovery/sandbox/bundle.js +6 -0
- package/dist/discovery/specifier-resolve.d.ts.map +1 -1
- package/dist/discovery/specifier-resolve.js +8 -2
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/fold/fold.js +68 -1
- package/dist/fold/subset.d.ts +37 -0
- package/dist/fold/subset.d.ts.map +1 -1
- package/dist/fold/subset.js +88 -0
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-detail.js +32 -7
- package/dist/health.d.ts +110 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +105 -0
- package/dist/health.schema.json +191 -0
- package/dist/lexicon-module.d.ts +10 -0
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lexicon-module.js +53 -8
- package/dist/lexicon-plugin-helpers.d.ts.map +1 -1
- package/dist/lexicon-plugin-helpers.js +6 -4
- package/dist/lexicon.d.ts +29 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/affected.d.ts.map +1 -1
- package/dist/lifecycle/affected.js +7 -1
- package/dist/lifecycle/merge-record.d.ts +104 -0
- package/dist/lifecycle/merge-record.d.ts.map +1 -0
- package/dist/lifecycle/merge-record.js +102 -0
- package/dist/lifecycle/publish-attachments.d.ts +8 -5
- package/dist/lifecycle/publish-attachments.d.ts.map +1 -1
- package/dist/lifecycle/publish-attachments.js +27 -13
- package/dist/lifecycle/publish-npm.d.ts +3 -2
- package/dist/lifecycle/publish-npm.d.ts.map +1 -1
- package/dist/lifecycle/publish-npm.js +4 -3
- package/dist/lifecycle/release-at.d.ts +40 -0
- package/dist/lifecycle/release-at.d.ts.map +1 -0
- package/dist/lifecycle/release-at.js +83 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -1
- package/dist/lifecycle/release-cut.js +23 -12
- package/dist/lifecycle/release-ledger.d.ts +21 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.js +24 -0
- package/dist/lifecycle/release-publish.d.ts +28 -12
- package/dist/lifecycle/release-publish.d.ts.map +1 -1
- package/dist/lifecycle/release-publish.js +29 -17
- package/dist/lifecycle/release-store.d.ts +25 -2
- package/dist/lifecycle/release-store.d.ts.map +1 -1
- package/dist/lifecycle/release-store.js +40 -13
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +1 -0
- package/dist/lifecycle/work-dir.d.ts +45 -0
- package/dist/lifecycle/work-dir.d.ts.map +1 -0
- package/dist/lifecycle/work-dir.js +78 -0
- package/dist/lint/discover.d.ts +17 -9
- package/dist/lint/discover.d.ts.map +1 -1
- package/dist/lint/discover.js +62 -54
- package/dist/lint/rules/op/index.d.ts +2 -1
- package/dist/lint/rules/op/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.js +4 -2
- package/dist/lint/rules/op/ops018-shell-image-digest.d.ts +10 -0
- package/dist/lint/rules/op/ops018-shell-image-digest.d.ts.map +1 -0
- package/dist/lint/rules/op/ops018-shell-image-digest.js +38 -0
- package/dist/module-source.d.ts +57 -0
- package/dist/module-source.d.ts.map +1 -0
- package/dist/module-source.js +104 -0
- package/dist/op/activities/activity-contracts.d.ts +5 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.js +3 -0
- package/dist/op/activities/factory.js +1 -1
- package/dist/op/activities/shell.d.ts +38 -0
- package/dist/op/activities/shell.d.ts.map +1 -1
- package/dist/op/activities/shell.js +89 -3
- package/dist/op/builders.d.ts +7 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +7 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/discover.js +13 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +3 -0
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +20 -5
- package/dist/op/operator.d.ts +2 -2
- package/dist/op/operator.js +2 -2
- package/dist/op/runtime.d.ts +2 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/steward.d.ts +6 -4
- package/dist/op/steward.d.ts.map +1 -1
- package/dist/op/steward.js +5 -3
- package/dist/op/work-lease-run.d.ts +20 -7
- package/dist/op/work-lease-run.d.ts.map +1 -1
- package/dist/op/work-lease-run.js +56 -12
- package/dist/pr-forge.d.ts +10 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +39 -0
- package/dist/pr-loop.d.ts +155 -7
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +137 -11
- package/dist/pr-report-url.d.ts +4 -4
- package/dist/pr-report-url.d.ts.map +1 -1
- package/dist/pr-report-url.js +5 -5
- package/dist/runner-start.d.ts +179 -0
- package/dist/runner-start.d.ts.map +1 -0
- package/dist/runner-start.js +277 -0
- package/dist/ts-loader.d.ts +38 -4
- package/dist/ts-loader.d.ts.map +1 -1
- package/dist/ts-loader.js +190 -16
- package/dist/wave-runtime.mjs +238 -231
- package/dist/workspace/chant-migrations/fountain-retired-kinds.d.ts +34 -0
- package/dist/workspace/chant-migrations/fountain-retired-kinds.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/fountain-retired-kinds.js +176 -0
- package/dist/workspace/chant-migrations.d.ts.map +1 -1
- package/dist/workspace/chant-migrations.js +2 -0
- package/dist/workspace/check.schema.json +70 -2
- package/dist/workspace/checks/boxes.d.ts +6 -6
- package/dist/workspace/checks/boxes.js +9 -9
- package/dist/workspace/checks/security.d.ts +35 -0
- package/dist/workspace/checks/security.d.ts.map +1 -0
- package/dist/workspace/checks/security.js +305 -0
- package/dist/workspace/checks.d.ts +2 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +5 -0
- package/dist/workspace/ci-builds.d.ts +163 -0
- package/dist/workspace/ci-builds.d.ts.map +1 -0
- package/dist/workspace/ci-builds.js +315 -0
- package/dist/workspace/ci-green-cli.d.ts +2 -1
- package/dist/workspace/ci-green-cli.d.ts.map +1 -1
- package/dist/workspace/ci-green-cli.js +25 -5
- package/dist/workspace/ci-green-forge.d.ts +18 -4
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green-forge.js +12 -0
- package/dist/workspace/ci-green-workflow.d.ts +55 -1
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +128 -1
- package/dist/workspace/ci-green.d.ts +117 -16
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +358 -43
- package/dist/workspace/ci-last-green.schema.json +49 -0
- package/dist/workspace/ci-list.schema.json +47 -7
- package/dist/workspace/ci-report-url.d.ts +18 -0
- package/dist/workspace/ci-report-url.d.ts.map +1 -0
- package/dist/workspace/ci-report-url.js +33 -0
- package/dist/workspace/ci-run-index.d.ts +13 -1
- package/dist/workspace/ci-run-index.d.ts.map +1 -1
- package/dist/workspace/ci-run-index.js +6 -4
- package/dist/workspace/ci-run-index.schema.json +6 -4
- package/dist/workspace/ci-run.d.ts +85 -6
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +262 -64
- package/dist/workspace/ci-runner-key.d.ts +16 -3
- package/dist/workspace/ci-runner-key.d.ts.map +1 -1
- package/dist/workspace/ci-runner-key.js +30 -10
- package/dist/workspace/ci-runner-start.d.ts +77 -0
- package/dist/workspace/ci-runner-start.d.ts.map +1 -0
- package/dist/workspace/ci-runner-start.js +185 -0
- package/dist/workspace/ci-serve.d.ts +130 -11
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +323 -40
- package/dist/workspace/declaration.d.ts +25 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +14 -2
- package/dist/workspace/declaration.schema.json +30 -2
- package/dist/workspace/export.d.ts +1 -1
- package/dist/workspace/export.d.ts.map +1 -1
- package/dist/workspace/export.js +7 -3
- package/dist/workspace/ls.d.ts +8 -1
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +2 -1
- package/dist/workspace/ls.schema.json +9 -2
- package/dist/workspace/member-pipeline.d.ts +1 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -1
- package/dist/workspace/member-pipeline.js +2 -0
- package/dist/workspace/merge-record.schema.json +49 -0
- package/dist/workspace/pin-integrity.d.ts.map +1 -1
- package/dist/workspace/pin-integrity.js +3 -2
- package/dist/workspace/pr-report.schema.json +75 -8
- package/dist/workspace/reason-codes.d.ts +18 -1
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +20 -2
- package/dist/workspace/release-candidates.d.ts +129 -0
- package/dist/workspace/release-candidates.d.ts.map +1 -0
- package/dist/workspace/release-candidates.js +233 -0
- package/dist/workspace/release-candidates.schema.json +353 -0
- package/dist/workspace/run-event.schema.json +3 -1
- package/dist/workspace/security.d.ts +40 -0
- package/dist/workspace/security.d.ts.map +1 -0
- package/dist/workspace/security.js +99 -0
- package/dist/workspace/status.schema.json +3 -1
- package/dist/workspace/write-scope.d.ts +9 -0
- package/dist/workspace/write-scope.d.ts.map +1 -1
- package/dist/workspace/write-scope.js +11 -1
- package/package.json +8 -1
- package/src/__fixtures__/pr-loop/pr-apply-wave-waiting.note.golden.md +48 -0
- package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +1 -1
- package/src/ci-jobs.ts +11 -2
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +3 -3
- package/src/cli/commands/audit.test.ts +2 -2
- package/src/cli/commands/check-lexicon-plugin.test.ts +9 -0
- package/src/cli/commands/check-lexicon-plugin.ts +11 -8
- package/src/cli/commands/check-lexicon.test.ts +1 -1
- package/src/cli/commands/check-lexicon.ts +16 -11
- package/src/cli/commands/import.ts +40 -0
- package/src/cli/commands/init-lexicon/templates/project.ts +4 -3
- package/src/cli/commands/init-lexicon.test.ts +8 -5
- package/src/cli/commands/onboard.test.ts +45 -0
- package/src/cli/commands/onboard.ts +8 -4
- package/src/cli/commands/update.test.ts +6 -0
- package/src/cli/commands/update.ts +4 -1
- package/src/cli/compiled-cli.e2e.test.ts +76 -11
- package/src/cli/dependencies.e2e.test.ts +101 -0
- package/src/cli/dependencies.test.ts +93 -0
- package/src/cli/dependencies.ts +122 -0
- package/src/cli/handlers/build.test.ts +105 -0
- package/src/cli/handlers/build.ts +35 -4
- package/src/cli/handlers/fan-out.test.ts +73 -0
- package/src/cli/handlers/fan-out.ts +11 -4
- package/src/cli/handlers/graph.test.ts +76 -0
- package/src/cli/handlers/graph.ts +19 -12
- package/src/cli/handlers/lifecycle-health.test.ts +123 -0
- package/src/cli/handlers/lifecycle-health.ts +157 -0
- package/src/cli/handlers/lifecycle.ts +1 -1
- package/src/cli/handlers/lint.test.ts +42 -0
- package/src/cli/handlers/lint.ts +6 -0
- package/src/cli/handlers/operator.ts +3 -3
- package/src/cli/handlers/pr-deploy-inputs.test.ts +209 -0
- package/src/cli/handlers/pr-record.test.ts +201 -0
- package/src/cli/handlers/pr-waves.test.ts +449 -0
- package/src/cli/handlers/pr.test.ts +156 -0
- package/src/cli/handlers/pr.ts +648 -33
- package/src/cli/handlers/promote.test.ts +43 -1
- package/src/cli/handlers/promote.ts +19 -3
- package/src/cli/handlers/release-cut-at.e2e.test.ts +115 -0
- package/src/cli/handlers/release-publish-targets.e2e.test.ts +2 -2
- package/src/cli/handlers/release-verify.e2e.test.ts +12 -10
- package/src/cli/handlers/release.e2e.test.ts +19 -28
- package/src/cli/handlers/release.ts +81 -11
- package/src/cli/handlers/run-components-exit.test.ts +99 -0
- package/src/cli/handlers/run-signer.test.ts +79 -0
- package/src/cli/handlers/run.ts +34 -16
- package/src/cli/main.test.ts +18 -0
- package/src/cli/main.ts +120 -33
- package/src/cli/parse-args.ts +28 -0
- package/src/cli/registry.ts +35 -8
- package/src/codegen/generate-post-synth-barrel.ts +2 -2
- package/src/codegen/lexicon-regen.ts +1 -1
- package/src/codegen/pinned-upgrade.ts +2 -2
- package/src/codegen/post-synth-barrel-guards.test.ts +6 -11
- package/src/components/capability.ts +51 -3
- package/src/components/changed-files.test.ts +16 -1
- package/src/components/changed-files.ts +9 -1
- package/src/components/deploy-inputs.test.ts +108 -0
- package/src/components/deploy-inputs.ts +225 -0
- package/src/components/driver.test.ts +36 -1
- package/src/components/driver.ts +87 -4
- package/src/components/fan-out-record.ts +29 -1
- package/src/components/fan-out-run.test.ts +35 -0
- package/src/components/fan-out-run.ts +40 -1
- package/src/components/gate-plan.ts +9 -0
- package/src/components/pr-pipeline.test.ts +100 -0
- package/src/components/pr-pipeline.ts +184 -28
- package/src/components/pr-run.test.ts +323 -18
- package/src/components/pr-run.ts +355 -76
- package/src/components/release-compile.test.ts +34 -0
- package/src/components/release-compile.ts +89 -8
- package/src/components/release-promote.test.ts +55 -2
- package/src/components/release-promote.ts +93 -23
- package/src/components/verbs/bom-writer.ts +1 -1
- package/src/components/verbs/lockfile-sbom-generator.test.ts +36 -0
- package/src/components/verbs/lockfile-sbom-generator.ts +43 -5
- package/src/components/wave-plan.ts +48 -8
- package/src/discovery/fold-host-method.test.ts +87 -0
- package/src/discovery/fold-import.ts +15 -5
- package/src/discovery/fold-package-composite.test.ts +27 -0
- package/src/discovery/fold-specifiers.test.ts +8 -0
- package/src/discovery/sandbox/bundle.ts +6 -0
- package/src/discovery/specifier-resolve.ts +8 -2
- package/src/fold/fold.test.ts +72 -0
- package/src/fold/fold.ts +71 -0
- package/src/fold/subset.test.ts +31 -0
- package/src/fold/subset.ts +99 -0
- package/src/graph-detail.test.ts +57 -0
- package/src/graph-detail.ts +33 -7
- package/src/health.schema.json +191 -0
- package/src/health.test.ts +62 -0
- package/src/health.ts +178 -0
- package/src/lexicon-dist.e2e.test.ts +254 -0
- package/src/lexicon-module.test.ts +47 -0
- package/src/lexicon-module.ts +49 -7
- package/src/lexicon-plugin-helpers.ts +6 -4
- package/src/lexicon.ts +43 -1
- package/src/lifecycle/affected.test.ts +7 -0
- package/src/lifecycle/affected.ts +6 -1
- package/src/lifecycle/merge-record.test.ts +111 -0
- package/src/lifecycle/merge-record.ts +154 -0
- package/src/lifecycle/publish-attachments.ts +25 -13
- package/src/lifecycle/publish-npm.ts +4 -3
- package/src/lifecycle/publish-targets.test.ts +34 -4
- package/src/lifecycle/release-at.test.ts +48 -0
- package/src/lifecycle/release-at.ts +100 -0
- package/src/lifecycle/release-cut.test.ts +41 -0
- package/src/lifecycle/release-cut.ts +22 -11
- package/src/lifecycle/release-ledger.ts +34 -0
- package/src/lifecycle/release-publish.test.ts +43 -13
- package/src/lifecycle/release-publish.ts +38 -18
- package/src/lifecycle/release-store.ts +57 -13
- package/src/lifecycle/run-ledger.test.ts +13 -0
- package/src/lifecycle/run-ledger.ts +1 -0
- package/src/lifecycle/work-dir.test.ts +77 -0
- package/src/lifecycle/work-dir.ts +82 -0
- package/src/lint/discover.test.ts +46 -2
- package/src/lint/discover.ts +55 -49
- package/src/lint/rules/op/index.ts +4 -2
- package/src/lint/rules/op/ops018-shell-image-digest.test.ts +60 -0
- package/src/lint/rules/op/ops018-shell-image-digest.ts +48 -0
- package/src/module-source.test.ts +85 -0
- package/src/module-source.ts +112 -0
- package/src/op/activities/activity-contracts.ts +3 -0
- package/src/op/activities/factory.ts +1 -1
- package/src/op/activities/shell-container.e2e.test.ts +67 -0
- package/src/op/activities/shell.test.ts +44 -1
- package/src/op/activities/shell.ts +110 -3
- package/src/op/builders.ts +7 -0
- package/src/op/discover-root.test.ts +31 -2
- package/src/op/discover.ts +14 -4
- package/src/op/gate.test.ts +30 -0
- package/src/op/gate.ts +3 -0
- package/src/op/op-waves-run.ts +20 -5
- package/src/op/op-waves.test.ts +49 -0
- package/src/op/operator.ts +2 -2
- package/src/op/runtime.ts +2 -0
- package/src/op/step-console.test.ts +4 -2
- package/src/op/steward.ts +6 -4
- package/src/op/work-lease-run.test.ts +88 -2
- package/src/op/work-lease-run.ts +52 -12
- package/src/pr-forge-bundle.test.ts +2 -2
- package/src/pr-forge.test.ts +42 -0
- package/src/pr-forge.ts +40 -0
- package/src/pr-loop-bundle.test.ts +2 -1
- package/src/pr-loop.test.ts +146 -1
- package/src/pr-loop.ts +278 -17
- package/src/pr-report-url.ts +7 -7
- package/src/runner-start-bundle.test.ts +29 -0
- package/src/runner-start.test.ts +179 -0
- package/src/runner-start.ts +329 -0
- package/src/terraform/__fixtures__/advise.test.ts +21 -2
- package/src/ts-loader.test.ts +89 -1
- package/src/ts-loader.ts +180 -13
- package/src/workspace/changes.test.ts +1 -1
- package/src/workspace/chant-migrations/fountain-retired-kinds.test.ts +33 -0
- package/src/workspace/chant-migrations/fountain-retired-kinds.ts +190 -0
- package/src/workspace/chant-migrations.e2e.test.ts +5 -0
- package/src/workspace/chant-migrations.ts +2 -0
- package/src/workspace/check-contract.test.ts +3 -2
- package/src/workspace/check.schema.json +70 -2
- package/src/workspace/checks/boxes.ts +9 -9
- package/src/workspace/checks/security.test.ts +138 -0
- package/src/workspace/checks/security.ts +305 -0
- package/src/workspace/checks.ts +5 -0
- package/src/workspace/ci-builds.e2e.test.ts +208 -0
- package/src/workspace/ci-builds.test.ts +174 -0
- package/src/workspace/ci-builds.ts +385 -0
- package/src/workspace/ci-green-builds.test.ts +180 -0
- package/src/workspace/ci-green-cli.test.ts +54 -0
- package/src/workspace/ci-green-cli.ts +26 -5
- package/src/workspace/ci-green-forge.ts +30 -4
- package/src/workspace/ci-green-runs.test.ts +53 -11
- package/src/workspace/ci-green-workflow.test.ts +84 -1
- package/src/workspace/ci-green-workflow.ts +139 -2
- package/src/workspace/ci-green.test.ts +6 -3
- package/src/workspace/ci-green.ts +408 -48
- package/src/workspace/ci-last-green.schema.json +49 -0
- package/src/workspace/ci-list.schema.json +47 -7
- package/src/workspace/ci-report-url.test.ts +220 -0
- package/src/workspace/ci-report-url.ts +33 -0
- package/src/workspace/ci-run-index.schema.json +6 -4
- package/src/workspace/ci-run-index.test.ts +42 -3
- package/src/workspace/ci-run-index.ts +19 -5
- package/src/workspace/ci-run.test.ts +203 -8
- package/src/workspace/ci-run.ts +295 -63
- package/src/workspace/ci-runner-key.test.ts +55 -17
- package/src/workspace/ci-runner-key.ts +36 -11
- package/src/workspace/ci-runner-start.test.ts +151 -0
- package/src/workspace/ci-runner-start.ts +193 -0
- package/src/workspace/ci-serve-builds.test.ts +86 -0
- package/src/workspace/ci-serve.test.ts +209 -6
- package/src/workspace/ci-serve.ts +341 -42
- package/src/workspace/declaration.schema.json +30 -2
- package/src/workspace/declaration.test.ts +40 -0
- package/src/workspace/declaration.ts +40 -3
- package/src/workspace/export.ts +6 -3
- package/src/workspace/ls.schema.json +9 -2
- package/src/workspace/ls.ts +10 -4
- package/src/workspace/member-pipeline.ts +2 -1
- package/src/workspace/merge-record.schema.json +49 -0
- package/src/workspace/pin-integrity.test.ts +5 -0
- package/src/workspace/pin-integrity.ts +3 -2
- package/src/workspace/pr-report.schema.json +75 -8
- package/src/workspace/reason-codes.test.ts +2 -0
- package/src/workspace/reason-codes.ts +20 -2
- package/src/workspace/release-candidates.schema.json +353 -0
- package/src/workspace/release-candidates.test.ts +106 -0
- package/src/workspace/release-candidates.ts +315 -0
- package/src/workspace/run-event.schema.json +3 -1
- package/src/workspace/security.test.ts +70 -0
- package/src/workspace/security.ts +118 -0
- package/src/workspace/status.schema.json +3 -1
- package/src/workspace/write-scope.test.ts +29 -1
- package/src/workspace/write-scope.ts +12 -1
|
@@ -68,9 +68,10 @@ import { approveCommand, gateIsSealed } from "../../op/gate";
|
|
|
68
68
|
import { getHeadCommit } from "../../lifecycle/git";
|
|
69
69
|
import { remainingFanOut, type ChangedUnits, type FanOutProgress } from "../../components/fan-out";
|
|
70
70
|
import { readDeployedOutputs } from "../../components/wave-plan";
|
|
71
|
-
import { deployContext } from "../../components/driver";
|
|
71
|
+
import { deployContext, failedRunExitCode } from "../../components/driver";
|
|
72
72
|
import { resolveCliBuildParams, parseParamFlags } from "../build-params-cli";
|
|
73
73
|
import { formatError, formatInfo, formatWarning } from "../format";
|
|
74
|
+
import { reportUninstalledDependencies } from "../dependencies";
|
|
74
75
|
import { FAN_OUT_GATE_OP } from "../../op/gate-name";
|
|
75
76
|
import { resolveBuildRoot } from "./lifecycle";
|
|
76
77
|
import { GATED_EXIT_CODE, autoReleaseRun, recordAutoReleasesForRun } from "./run";
|
|
@@ -237,10 +238,14 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
|
|
|
237
238
|
} catch (err) {
|
|
238
239
|
// A cycle or an unknown `dependsOn` refuses here, over the whole graph and
|
|
239
240
|
// before anything is selected (#2417). The message names the members.
|
|
240
|
-
|
|
241
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
242
|
+
reportUninstalledDependencies([message], projectPath); // #4116
|
|
243
|
+
console.error(formatError({ message }));
|
|
241
244
|
return 1;
|
|
242
245
|
}
|
|
243
246
|
if (!derived.success) {
|
|
247
|
+
// #4116: component files that import packages the project has not installed.
|
|
248
|
+
if (derived.error) reportUninstalledDependencies([derived.error], projectPath);
|
|
244
249
|
console.error(formatError({ message: derived.error ?? "Could not discover components" }));
|
|
245
250
|
return 1;
|
|
246
251
|
}
|
|
@@ -522,8 +527,10 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
|
|
|
522
527
|
...(await summaryLedgerPrefix()),
|
|
523
528
|
...(pending.environment ? { environment: pending.environment } : {}),
|
|
524
529
|
});
|
|
525
|
-
return result.status === "gated" ? GATED_EXIT_CODE :
|
|
530
|
+
return result.status === "gated" ? GATED_EXIT_CODE : failedRunExitCode(result.results);
|
|
526
531
|
}
|
|
527
532
|
|
|
528
|
-
|
|
533
|
+
// A step that refused the wave's plan because its target moved since the
|
|
534
|
+
// approval exits 3, the code of a wave whose plan changed (#3997).
|
|
535
|
+
return result.status === "ok" ? 0 : failedRunExitCode(result.results);
|
|
529
536
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdirSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
2
5
|
import type { ParsedArgs } from "../registry";
|
|
3
6
|
import { DECLARABLE_MARKER, type Declarable } from "../../declarable";
|
|
4
7
|
import { AttrRef } from "../../attrref";
|
|
@@ -468,6 +471,79 @@ describe("runGraph", () => {
|
|
|
468
471
|
expect(discoverMock).not.toHaveBeenCalled();
|
|
469
472
|
});
|
|
470
473
|
|
|
474
|
+
// #4116: a clone of examples/fan-out-estate with no node_modules. Every
|
|
475
|
+
// source file fails to import its lexicon, and lint reports each import.
|
|
476
|
+
describe("lint gate: dependencies not installed (#4116)", () => {
|
|
477
|
+
let project: string;
|
|
478
|
+
beforeEach(() => {
|
|
479
|
+
project = realpathSync(mkdtempSync(join(tmpdir(), "chant-4116-graph-")));
|
|
480
|
+
});
|
|
481
|
+
afterEach(() => {
|
|
482
|
+
rmSync(project, { recursive: true, force: true });
|
|
483
|
+
});
|
|
484
|
+
const missing = (pkg: string, file: string) => ({
|
|
485
|
+
file: join(project, file), line: 1, column: 1, ruleId: "LEX002", severity: "error",
|
|
486
|
+
message: `Cannot find package '${pkg}' imported from ${join(project, file)}`,
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("every lint error an uninstalled import: the dependencies message, not lint errors", async () => {
|
|
490
|
+
lintMock.mockResolvedValue({
|
|
491
|
+
success: false,
|
|
492
|
+
diagnostics: [
|
|
493
|
+
missing("@intentius/chant-lexicon-aws", "src/network/outputs.ts"),
|
|
494
|
+
{ ...missing("@intentius/chant", "network.component.ts"), ruleId: "COMP000", message: `Component discovery error: ${missing("@intentius/chant", "network.component.ts").message}` },
|
|
495
|
+
{ ...missing("@intentius/chant", "x.ts"), ruleId: "COR004", severity: "warning", message: "a warning" },
|
|
496
|
+
],
|
|
497
|
+
});
|
|
498
|
+
for (const components of [false, true]) {
|
|
499
|
+
stderrBuf.length = 0;
|
|
500
|
+
const exit = await runGraph({ args: makeArgs({ format: "ir", path: project, components }), plugins: [], serializers: [] });
|
|
501
|
+
expect(exit).toBe(1);
|
|
502
|
+
const err = stderrBuf.join("\n");
|
|
503
|
+
expect(err).toContain(`dependencies not installed: @intentius/chant-lexicon-aws does not resolve from ${project}`);
|
|
504
|
+
expect(err).toContain("run npm install (or the project's package manager)");
|
|
505
|
+
expect(err).not.toMatch(/lint errors/i);
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test("an uninstalled import beside a real lint error: both are said", async () => {
|
|
510
|
+
lintMock.mockResolvedValue({
|
|
511
|
+
success: false,
|
|
512
|
+
diagnostics: [
|
|
513
|
+
missing("@intentius/chant-lexicon-aws", "src/a.ts"),
|
|
514
|
+
{ file: join(project, "src/b.ts"), line: 3, column: 1, ruleId: "COR001", severity: "error", message: "a real problem" },
|
|
515
|
+
],
|
|
516
|
+
});
|
|
517
|
+
const exit = await runGraph({ args: makeArgs({ format: "ir", path: project }), plugins: [], serializers: [] });
|
|
518
|
+
expect(exit).toBe(1);
|
|
519
|
+
const err = stderrBuf.join("\n");
|
|
520
|
+
expect(err).toContain("dependencies not installed: @intentius/chant-lexicon-aws");
|
|
521
|
+
expect(err).toMatch(/source has lint errors/);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
test("real lint errors still say lint errors, with no dependencies message", async () => {
|
|
525
|
+
lintMock.mockResolvedValue({
|
|
526
|
+
success: false,
|
|
527
|
+
diagnostics: [{ file: join(project, "src/b.ts"), line: 3, column: 1, ruleId: "COR001", severity: "error", message: "a real problem" }],
|
|
528
|
+
});
|
|
529
|
+
const exit = await runGraph({ args: makeArgs({ format: "ir", path: project }), plugins: [], serializers: [] });
|
|
530
|
+
expect(exit).toBe(1);
|
|
531
|
+
const err = stderrBuf.join("\n");
|
|
532
|
+
expect(err).toMatch(/source has lint errors/);
|
|
533
|
+
expect(err).not.toContain("dependencies not installed");
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
test("an import the project has installed is a lint error, not a missing dependency", async () => {
|
|
537
|
+
mkdirSync(join(project, "node_modules", "@intentius", "chant-lexicon-aws"), { recursive: true });
|
|
538
|
+
lintMock.mockResolvedValue({ success: false, diagnostics: [missing("@intentius/chant-lexicon-aws", "src/a.ts")] });
|
|
539
|
+
const exit = await runGraph({ args: makeArgs({ format: "ir", path: project }), plugins: [], serializers: [] });
|
|
540
|
+
expect(exit).toBe(1);
|
|
541
|
+
const err = stderrBuf.join("\n");
|
|
542
|
+
expect(err).toMatch(/source has lint errors/);
|
|
543
|
+
expect(err).not.toContain("dependencies not installed");
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
|
|
471
547
|
test("--format mermaid emits a flowchart", async () => {
|
|
472
548
|
lintClean(); discovered();
|
|
473
549
|
const exit = await runGraph({ args: makeArgs({ format: "mermaid" }), plugins: [], serializers: [] });
|
|
@@ -19,7 +19,8 @@ import { applyLens, parseLens } from "../../graph-lens";
|
|
|
19
19
|
import { toMermaid } from "../../graph-mermaid";
|
|
20
20
|
import { toDot } from "../../graph-dot";
|
|
21
21
|
import { getLayoutEngine, toLayoutInput, type NodeSize } from "../../graph-layout";
|
|
22
|
-
import { lintCommand } from "../commands/lint";
|
|
22
|
+
import { lintCommand, type LintResult } from "../commands/lint";
|
|
23
|
+
import { reportUninstalledDependencies } from "../dependencies";
|
|
23
24
|
import { loadPlugins, resolveProjectLexicons, collectBuildRootContributors } from "../plugins";
|
|
24
25
|
import { isNoLexiconDetected } from "../../detectLexicon";
|
|
25
26
|
import { readFileSync } from "node:fs";
|
|
@@ -555,6 +556,21 @@ async function runComponentGraph(ctx: CommandContext): Promise<number> {
|
|
|
555
556
|
return 0;
|
|
556
557
|
}
|
|
557
558
|
|
|
559
|
+
/**
|
|
560
|
+
* The lint gate's refusal. When the lint errors are imports of packages the
|
|
561
|
+
* project has not installed (#4116), the dependencies message leads, and it
|
|
562
|
+
* stands alone when every error is one: the source is not what is wrong.
|
|
563
|
+
*/
|
|
564
|
+
function refuseLintGate(lint: LintResult, projectPath: string): void {
|
|
565
|
+
const errors = (lint.diagnostics ?? []).filter((d) => d.severity === "error").map((d) => d.message);
|
|
566
|
+
if (reportUninstalledDependencies(errors, projectPath) === "all") return;
|
|
567
|
+
console.error(
|
|
568
|
+
formatError({
|
|
569
|
+
message: "Refusing to emit graph: source has lint errors. Run `chant lint` and fix them first.",
|
|
570
|
+
}),
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
|
|
558
574
|
/**
|
|
559
575
|
* `chant graph --components --format ir|mermaid|dot|layout` — the component DAG
|
|
560
576
|
* projected into the renderable IR (nodes = components, `groups.byWave` = the
|
|
@@ -596,11 +612,7 @@ async function runComponentGraphView(
|
|
|
596
612
|
buildParams: componentParams,
|
|
597
613
|
});
|
|
598
614
|
if (!lint.success) {
|
|
599
|
-
|
|
600
|
-
formatError({
|
|
601
|
-
message: "Refusing to emit graph: source has lint errors. Run `chant lint` and fix them first.",
|
|
602
|
-
}),
|
|
603
|
-
);
|
|
615
|
+
refuseLintGate(lint, projectPath);
|
|
604
616
|
return 1;
|
|
605
617
|
}
|
|
606
618
|
|
|
@@ -869,12 +881,7 @@ async function runGraphView(
|
|
|
869
881
|
buildParams,
|
|
870
882
|
});
|
|
871
883
|
if (!lint.success) {
|
|
872
|
-
|
|
873
|
-
formatError({
|
|
874
|
-
message:
|
|
875
|
-
"Refusing to emit graph: source has lint errors. Run `chant lint` and fix them first.",
|
|
876
|
-
}),
|
|
877
|
-
);
|
|
884
|
+
refuseLintGate(lint, projectPath);
|
|
878
885
|
return 1;
|
|
879
886
|
}
|
|
880
887
|
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { createMockPlugin } from "@intentius/chant-test-utils";
|
|
3
|
+
import type { BuildResult } from "../../build";
|
|
4
|
+
import type { LexiconPlugin } from "../../lexicon";
|
|
5
|
+
import type { ParsedArgs } from "../registry";
|
|
6
|
+
import { HEALTH_SCHEMA_ID } from "../../health";
|
|
7
|
+
import { UNOBSERVED_REASONS } from "../../observation";
|
|
8
|
+
import { contract, validSchema } from "../../workspace/__fixtures__/contract-repo";
|
|
9
|
+
import schema from "../../health.schema.json";
|
|
10
|
+
|
|
11
|
+
const { expectValid, validate } = contract(schema);
|
|
12
|
+
|
|
13
|
+
const buildMock = vi.fn();
|
|
14
|
+
const loadChantConfigMock = vi.fn();
|
|
15
|
+
vi.mock("../../build", () => ({ build: (...args: unknown[]) => buildMock(...args) }));
|
|
16
|
+
vi.mock("../../config", async () => {
|
|
17
|
+
const actual = await vi.importActual<typeof import("../../config")>("../../config");
|
|
18
|
+
return { ...actual, loadChantConfig: (...args: unknown[]) => loadChantConfigMock(...args) };
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const { runLifecycleHealth } = await import("./lifecycle-health");
|
|
22
|
+
|
|
23
|
+
function args(overrides: Partial<ParsedArgs> = {}): ParsedArgs {
|
|
24
|
+
return { command: "lifecycle", path: "health", format: "", fix: false, watch: false, verbose: false, help: false, extraPositional: "prod", ...overrides } as ParsedArgs;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function buildResult(entities: Array<[string, string, string]>): BuildResult {
|
|
28
|
+
return {
|
|
29
|
+
outputs: new Map([["aws", "{}"]]),
|
|
30
|
+
entities: new Map(entities.map(([name, lexicon, entityType]) => [name, { lexicon, entityType, kind: "resource", props: {} }])),
|
|
31
|
+
dependencies: new Map(),
|
|
32
|
+
errors: [],
|
|
33
|
+
warnings: [],
|
|
34
|
+
manifest: { lexicons: ["aws"], outputs: {}, deployOrder: ["aws"] },
|
|
35
|
+
sourceFileCount: 1,
|
|
36
|
+
} as unknown as BuildResult;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("chant lifecycle health (#4177)", () => {
|
|
40
|
+
let out: string[];
|
|
41
|
+
let err: string[];
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
out = [];
|
|
44
|
+
err = [];
|
|
45
|
+
vi.spyOn(console, "log").mockImplementation((s: string) => { out.push(s); });
|
|
46
|
+
vi.spyOn(console, "error").mockImplementation((s: string) => { err.push(s); });
|
|
47
|
+
loadChantConfigMock.mockResolvedValue({ config: { environments: ["prod"] } });
|
|
48
|
+
buildMock.mockReset();
|
|
49
|
+
});
|
|
50
|
+
afterEach(() => vi.restoreAllMocks());
|
|
51
|
+
|
|
52
|
+
test("asks only about the types the lexicon names, and prints chant-health/v1", async () => {
|
|
53
|
+
buildMock.mockResolvedValue(buildResult([["web", "aws", "AWS::ECS::Service"], ["bucket", "aws", "AWS::S3::Bucket"]]));
|
|
54
|
+
const asked: string[][] = [];
|
|
55
|
+
const plugin: LexiconPlugin = {
|
|
56
|
+
...createMockPlugin({ name: "aws" }),
|
|
57
|
+
healthTypes: ["AWS::ECS::Service"],
|
|
58
|
+
observeHealth: async (o) => {
|
|
59
|
+
asked.push([...o.entities.keys()]);
|
|
60
|
+
return { entities: { web: { type: "AWS::ECS::Service", state: "unhealthy", detail: "0/2 running", observedAt: "t" } }, unobserved: {}, absent: [] };
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
const exit = await runLifecycleHealth({ args: args({ json: true }), plugins: [plugin], serializers: [plugin.serializer] });
|
|
64
|
+
expect(exit).toBe(0);
|
|
65
|
+
expect(asked).toEqual([["web"]]);
|
|
66
|
+
const json = JSON.parse(out[0]!);
|
|
67
|
+
expect(json.schema).toBe("chant-health/v1");
|
|
68
|
+
expect(typeof json.chantVersion).toBe("string");
|
|
69
|
+
expect(json.lexicons.aws.entities.web.state).toBe("unhealthy");
|
|
70
|
+
expect(json.summary).toEqual({ healthy: 0, unhealthy: 1, unknown: 0, unobserved: 0, absent: 0 });
|
|
71
|
+
expectValid(json);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("a reader that throws is not read, never unhealthy", async () => {
|
|
75
|
+
buildMock.mockResolvedValue(buildResult([["web", "aws", "AWS::ECS::Service"]]));
|
|
76
|
+
const plugin: LexiconPlugin = {
|
|
77
|
+
...createMockPlugin({ name: "aws" }),
|
|
78
|
+
healthTypes: ["AWS::ECS::Service"],
|
|
79
|
+
observeHealth: async () => {
|
|
80
|
+
throw new Error("no route to host");
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
await runLifecycleHealth({ args: args(), plugins: [plugin], serializers: [plugin.serializer] });
|
|
84
|
+
expect(out.join("\n")).toContain("not read web (AWS::ECS::Service): read-failed, no route to host");
|
|
85
|
+
expect(err.join("\n")).toContain("0 healthy, 0 unhealthy, 0 unknown, 1 not observed");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("refuses with no environment, and with no lexicon that reads health", async () => {
|
|
89
|
+
const plain = createMockPlugin({ name: "aws" });
|
|
90
|
+
expect(await runLifecycleHealth({ args: args({ extraPositional: undefined }), plugins: [plain], serializers: [] })).toBe(1);
|
|
91
|
+
expect(await runLifecycleHealth({ args: args(), plugins: [plain], serializers: [] })).toBe(1);
|
|
92
|
+
expect(err.join("\n")).toContain("No configured lexicon has a health read");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("the JSON of a read that failed follows the schema", async () => {
|
|
96
|
+
buildMock.mockResolvedValue(buildResult([["web", "aws", "AWS::ECS::Service"], ["gone", "aws", "AWS::ECS::Service"]]));
|
|
97
|
+
const plugin: LexiconPlugin = {
|
|
98
|
+
...createMockPlugin({ name: "aws" }),
|
|
99
|
+
healthTypes: ["AWS::ECS::Service"],
|
|
100
|
+
observeHealth: async () => ({ entities: {}, unobserved: { web: { type: "AWS::ECS::Service", reason: "no-credentials", detail: "no AWS credentials" } }, absent: ["gone"] }),
|
|
101
|
+
};
|
|
102
|
+
await runLifecycleHealth({ args: args({ json: true }), plugins: [plugin], serializers: [plugin.serializer] });
|
|
103
|
+
const json = JSON.parse(out[0]!);
|
|
104
|
+
expect(json.summary).toEqual({ healthy: 0, unhealthy: 0, unknown: 0, unobserved: 1, absent: 1 });
|
|
105
|
+
expectValid(json);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe("the chant-health/v1 schema (#4177)", () => {
|
|
110
|
+
test("is a valid schema with the exported id, and names every unobserved reason", () => {
|
|
111
|
+
expect(validSchema(schema)).toBe(true);
|
|
112
|
+
expect(schema.$id).toBe(HEALTH_SCHEMA_ID);
|
|
113
|
+
expect([...schema.$defs.hole.properties.reason.enum].sort()).toEqual([...UNOBSERVED_REASONS].sort());
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("a document with neither lexicons nor stacks, or a state outside the three, does not validate", () => {
|
|
117
|
+
const base = { schema: "chant-health/v1", chantVersion: "0.124.33", environment: "prod", observedAt: "t", summary: { healthy: 0, unhealthy: 0, unknown: 0, unobserved: 0, absent: 0 } };
|
|
118
|
+
expect(validate(base)).toBe(false);
|
|
119
|
+
expectValid({ ...base, stacks: { east: {} } });
|
|
120
|
+
const bad = { ...base, lexicons: { aws: { entities: { web: { type: "AWS::ECS::Service", state: "down", detail: "", observedAt: "t" } }, unobserved: {}, absent: [] } } };
|
|
121
|
+
expect(validate(bad)).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant lifecycle health <env> [lexicon] [--json]` (#4177): whether each
|
|
3
|
+
* declared resource that has a health read is up, separate from drift.
|
|
4
|
+
*
|
|
5
|
+
* It builds the project as `lifecycle diff` does, so the entity names are the
|
|
6
|
+
* graph's node ids, then asks each lexicon with a health read about the
|
|
7
|
+
* entities whose types it names. An environment's declared endpoint is applied
|
|
8
|
+
* for the read, as on the other live reads (#1166).
|
|
9
|
+
*
|
|
10
|
+
* The JSON is the {@link HEALTH_SCHEMA} shape. A single-stack project prints
|
|
11
|
+
* `lexicons` at the top level; a multi-stack one nests them under `stacks`,
|
|
12
|
+
* the same split `lifecycle diff --live --json` makes.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { resolve } from "node:path";
|
|
16
|
+
import { build } from "../../build";
|
|
17
|
+
import { loadChantConfig } from "../../config";
|
|
18
|
+
import { isObservableDeclarable } from "../../declarable";
|
|
19
|
+
import { HEALTH_SCHEMA, healthApplicable, observeHealthFor, summarizeHealth, type HealthLexicon, type HealthReadResult } from "../../health";
|
|
20
|
+
import { applyLiveEndpoint } from "../../live-endpoint";
|
|
21
|
+
import type { SerializerResult } from "../../serializer";
|
|
22
|
+
import { CHANT_VERSION } from "../version";
|
|
23
|
+
import { commandBuildParams } from "../build-params-cli";
|
|
24
|
+
import { resolveBuildModes, resolveProjectBuildOptions } from "../build-options";
|
|
25
|
+
import { formatBold, formatError, formatWarning } from "../format";
|
|
26
|
+
import type { CommandContext } from "../registry";
|
|
27
|
+
import { resolveStackTargets } from "./lifecycle";
|
|
28
|
+
|
|
29
|
+
type ByLexicon = Record<string, HealthReadResult>;
|
|
30
|
+
|
|
31
|
+
export async function runLifecycleHealth(ctx: CommandContext): Promise<number> {
|
|
32
|
+
const { args, plugins } = ctx;
|
|
33
|
+
const environment = args.extraPositional;
|
|
34
|
+
const lexiconFilter = args.extraPositional2;
|
|
35
|
+
if (!environment) {
|
|
36
|
+
console.error(formatError({ message: "Environment is required: chant lifecycle health <environment> [lexicon]" }));
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const readers = plugins.filter(
|
|
41
|
+
(p) => p.observeHealth && (p.healthTypes?.length ?? 0) > 0 && (!lexiconFilter || p.name === lexiconFilter),
|
|
42
|
+
);
|
|
43
|
+
if (readers.length === 0) {
|
|
44
|
+
console.error(formatWarning({
|
|
45
|
+
message: lexiconFilter
|
|
46
|
+
? `${lexiconFilter}: no health read in this lexicon`
|
|
47
|
+
: "No configured lexicon has a health read (aws, azure and gcp do)",
|
|
48
|
+
}));
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const projectRoot = resolve(".");
|
|
53
|
+
const { config } = await loadChantConfig(projectRoot);
|
|
54
|
+
const declaredParams = await commandBuildParams(config.buildParams, args);
|
|
55
|
+
if (!declaredParams) return 1;
|
|
56
|
+
const targets = resolveStackTargets(args, config);
|
|
57
|
+
const buildOptions = resolveProjectBuildOptions({
|
|
58
|
+
config,
|
|
59
|
+
configDir: projectRoot,
|
|
60
|
+
plugins,
|
|
61
|
+
modes: resolveBuildModes(config, { fold: args.fold, sandbox: args.sandbox }),
|
|
62
|
+
buildParams: declaredParams,
|
|
63
|
+
});
|
|
64
|
+
const serializers = readers.map((p) => p.serializer);
|
|
65
|
+
|
|
66
|
+
const endpoint = applyLiveEndpoint(config.environments, environment, readers);
|
|
67
|
+
if (endpoint.notice) console.error(formatWarning({ message: endpoint.notice }));
|
|
68
|
+
|
|
69
|
+
const perStack: Record<string, ByLexicon> = {};
|
|
70
|
+
let single: ByLexicon | undefined;
|
|
71
|
+
let anyBuildError = false;
|
|
72
|
+
const observedAt = new Date().toISOString();
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
for (const target of targets) {
|
|
76
|
+
const buildResult = await build(target.root, serializers, undefined, buildOptions);
|
|
77
|
+
if (buildResult.errors.length > 0) {
|
|
78
|
+
console.error(formatError({
|
|
79
|
+
message: `Build failed for ${target.stack ? `stack "${target.stack}"` : "project"}; fix errors before reading health`,
|
|
80
|
+
}));
|
|
81
|
+
anyBuildError = true;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const byLexicon: ByLexicon = {};
|
|
85
|
+
for (const plugin of readers) {
|
|
86
|
+
const declared = new Map<string, { entityType: string; props: Record<string, unknown> }>();
|
|
87
|
+
for (const [name, entity] of buildResult.entities) {
|
|
88
|
+
if (entity.lexicon === plugin.name && isObservableDeclarable(entity)) {
|
|
89
|
+
declared.set(name, {
|
|
90
|
+
entityType: entity.entityType,
|
|
91
|
+
props: (entity.props != null ? entity.props : {}) as Record<string, unknown>,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const entities = healthApplicable(plugin as HealthLexicon, declared);
|
|
96
|
+
if (entities.size === 0) continue;
|
|
97
|
+
const raw = buildResult.outputs.get(plugin.name);
|
|
98
|
+
const buildOutput = raw === undefined ? "" : typeof raw === "string" ? raw : (raw as SerializerResult).primary;
|
|
99
|
+
byLexicon[plugin.name] = await observeHealthFor(plugin as HealthLexicon, {
|
|
100
|
+
environment,
|
|
101
|
+
buildOutput,
|
|
102
|
+
entities,
|
|
103
|
+
...(target.stack ? { stack: target.stack } : {}),
|
|
104
|
+
...(target.region ? { region: target.region } : {}),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (target.stack) perStack[target.stack] = byLexicon;
|
|
108
|
+
else single = byLexicon;
|
|
109
|
+
}
|
|
110
|
+
} finally {
|
|
111
|
+
endpoint.restore();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const all = single ? [single] : Object.values(perStack);
|
|
115
|
+
const summary = summarizeHealth(all.flatMap((b) => Object.values(b)));
|
|
116
|
+
|
|
117
|
+
if (args.json) {
|
|
118
|
+
console.log(JSON.stringify({
|
|
119
|
+
schema: HEALTH_SCHEMA,
|
|
120
|
+
chantVersion: CHANT_VERSION,
|
|
121
|
+
environment,
|
|
122
|
+
observedAt,
|
|
123
|
+
...(single ? { lexicons: single } : { stacks: perStack }),
|
|
124
|
+
summary,
|
|
125
|
+
}));
|
|
126
|
+
} else {
|
|
127
|
+
if (single) renderHealth(single);
|
|
128
|
+
else for (const [stack, byLexicon] of Object.entries(perStack)) {
|
|
129
|
+
console.log(`\n${formatBold(`■ stack ${stack}`)}`);
|
|
130
|
+
renderHealth(byLexicon);
|
|
131
|
+
}
|
|
132
|
+
const total = summary.healthy + summary.unhealthy + summary.unknown + summary.unobserved + summary.absent;
|
|
133
|
+
if (total === 0) {
|
|
134
|
+
console.error(formatWarning({ message: "No declared resource has a health read" }));
|
|
135
|
+
} else {
|
|
136
|
+
console.error(
|
|
137
|
+
`${summary.healthy} healthy, ${summary.unhealthy} unhealthy, ${summary.unknown} unknown` +
|
|
138
|
+
(summary.unobserved > 0 ? `, ${summary.unobserved} not observed` : "") +
|
|
139
|
+
(summary.absent > 0 ? `, ${summary.absent} not deployed` : ""),
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return anyBuildError ? 1 : 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function renderHealth(byLexicon: ByLexicon): void {
|
|
147
|
+
for (const [lexicon, result] of Object.entries(byLexicon)) {
|
|
148
|
+
console.log(formatBold(lexicon));
|
|
149
|
+
for (const [name, entry] of Object.entries(result.entities)) {
|
|
150
|
+
console.log(` ${entry.state.padEnd(9)} ${name} (${entry.type}): ${entry.detail}`);
|
|
151
|
+
}
|
|
152
|
+
for (const [name, hole] of Object.entries(result.unobserved)) {
|
|
153
|
+
console.log(` ${"not read".padEnd(9)} ${name}${hole.type ? ` (${hole.type})` : ""}: ${hole.reason}${hole.detail ? `, ${hole.detail}` : ""}`);
|
|
154
|
+
}
|
|
155
|
+
for (const name of result.absent) console.log(` ${"absent".padEnd(9)} ${name}: not deployed`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -90,7 +90,7 @@ interface StackTarget {
|
|
|
90
90
|
* observed as the stack named after the environment (unchanged behavior). An
|
|
91
91
|
* explicit `--src` always wins and forces a single scoped target.
|
|
92
92
|
*/
|
|
93
|
-
function resolveStackTargets(args: ParsedArgs, config: ChantConfig): StackTarget[] {
|
|
93
|
+
export function resolveStackTargets(args: ParsedArgs, config: ChantConfig): StackTarget[] {
|
|
94
94
|
if (args.src) return [{ root: resolve(args.src) }];
|
|
95
95
|
if (config.stacks && config.stacks.length > 0) {
|
|
96
96
|
return config.stacks.map((s) => ({ stack: s.name, root: resolve(s.src), region: s.region }));
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
* #1108 gap in generate mode.
|
|
17
17
|
*/
|
|
18
18
|
import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
|
|
19
|
+
import { mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
20
|
+
import { tmpdir } from "node:os";
|
|
21
|
+
import { join } from "node:path";
|
|
19
22
|
import type { ParsedArgs } from "../registry";
|
|
20
23
|
|
|
21
24
|
const lintCommandMock = vi.fn();
|
|
@@ -105,3 +108,42 @@ describe("runLint build-time parameters (#2251)", () => {
|
|
|
105
108
|
expect(lintCommandMock).not.toHaveBeenCalled();
|
|
106
109
|
});
|
|
107
110
|
});
|
|
111
|
+
|
|
112
|
+
describe("runLint on a project whose dependencies are not installed (#4116)", () => {
|
|
113
|
+
let project: string;
|
|
114
|
+
let stderr: string[];
|
|
115
|
+
|
|
116
|
+
beforeEach(() => {
|
|
117
|
+
project = realpathSync(mkdtempSync(join(tmpdir(), "chant-4116-lint-")));
|
|
118
|
+
stderr = [];
|
|
119
|
+
loadChantConfigUpwardMock.mockReset().mockResolvedValue({ config: {} });
|
|
120
|
+
vi.spyOn(console, "error").mockImplementation((s: string) => void stderr.push(s));
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
afterEach(() => {
|
|
124
|
+
vi.restoreAllMocks();
|
|
125
|
+
rmSync(project, { recursive: true, force: true });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const failing = (message: string) =>
|
|
129
|
+
lintCommandMock.mockReset().mockResolvedValue({
|
|
130
|
+
success: false,
|
|
131
|
+
errorCount: 1,
|
|
132
|
+
warningCount: 0,
|
|
133
|
+
diagnostics: [{ file: join(project, "src/a.ts"), line: 1, column: 1, ruleId: "LEX002", severity: "error", message }],
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("an uninstalled import leads with the dependencies message", async () => {
|
|
137
|
+
failing(`Cannot find package '@intentius/chant-lexicon-aws' imported from ${join(project, "src/a.ts")}`);
|
|
138
|
+
const exit = await runLint({ args: makeArgs({ path: project }), plugins: [], serializers: [] });
|
|
139
|
+
expect(exit).toBe(1);
|
|
140
|
+
expect(stderr.join("\n")).toContain(`dependencies not installed: @intentius/chant-lexicon-aws does not resolve from ${project}`);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("a real lint error prints no dependencies message", async () => {
|
|
144
|
+
failing("Exported declarable 'x' is broken");
|
|
145
|
+
const exit = await runLint({ args: makeArgs({ path: project }), plugins: [], serializers: [] });
|
|
146
|
+
expect(exit).toBe(1);
|
|
147
|
+
expect(stderr.join("\n")).not.toContain("dependencies not installed");
|
|
148
|
+
});
|
|
149
|
+
});
|
package/src/cli/handlers/lint.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { lintCommand, lintCommandWatch, printLintResult } from "../commands/lint";
|
|
3
3
|
import { formatError, formatInfo } from "../format";
|
|
4
|
+
import { reportUninstalledDependencies } from "../dependencies";
|
|
4
5
|
import type { CommandContext } from "../registry";
|
|
5
6
|
import { commandBuildParams } from "../build-params-cli";
|
|
6
7
|
import { loadChantConfigUpward, type ChantConfig } from "../../config";
|
|
@@ -63,6 +64,11 @@ export async function runLint(ctx: CommandContext): Promise<number> {
|
|
|
63
64
|
buildParams,
|
|
64
65
|
});
|
|
65
66
|
|
|
67
|
+
// #4116: errors that are imports of uninstalled packages are led by the dependencies message.
|
|
68
|
+
if (!result.success) {
|
|
69
|
+
const errors = result.diagnostics.filter((d) => d.severity === "error").map((d) => d.message);
|
|
70
|
+
reportUninstalledDependencies(errors, resolve(args.path));
|
|
71
|
+
}
|
|
66
72
|
printLintResult(result);
|
|
67
73
|
return result.success ? 0 : 1;
|
|
68
74
|
}
|
|
@@ -221,8 +221,8 @@ export async function runOperator(ctx: CommandContext): Promise<number> {
|
|
|
221
221
|
*
|
|
222
222
|
* Refuses, before ticking anything:
|
|
223
223
|
* - an environment whose form is `fountain`, because there the composite's
|
|
224
|
-
* Agent and
|
|
225
|
-
* writer beside them;
|
|
224
|
+
* Agent and its conversation are the steward and this process would be a
|
|
225
|
+
* second writer beside them;
|
|
226
226
|
* - a steward whose lease another live process holds (a second local
|
|
227
227
|
* operator for it).
|
|
228
228
|
*/
|
|
@@ -242,7 +242,7 @@ export async function runStewardOperator(ctx: CommandContext): Promise<number> {
|
|
|
242
242
|
if (form !== "local") {
|
|
243
243
|
console.error(formatError({
|
|
244
244
|
message: `steward "${steward.name}" runs on ${form} in environment "${env}", so a local operator for it would be a second writer`,
|
|
245
|
-
hint: `Its turns run
|
|
245
|
+
hint: `Its turns run in the fountain steward "${steward.name}"'s conversation. Run this in an environment the declaration's form makes local, or change the form.`,
|
|
246
246
|
}));
|
|
247
247
|
return 1;
|
|
248
248
|
}
|