@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
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chant #4116, end to end through the real CLI: a project whose dependencies
|
|
3
|
+
* are not installed is told so, and a project without node_modules that builds
|
|
4
|
+
* today still builds.
|
|
5
|
+
*
|
|
6
|
+
* Both fixtures are temporary git repositories with no package.json install,
|
|
7
|
+
* run with this checkout's chant from source the way
|
|
8
|
+
* ../workspace/member-commands.e2e.test.ts runs it:
|
|
9
|
+
*
|
|
10
|
+
* - `bare` is a clone of a project like examples/fan-out-estate: its source
|
|
11
|
+
* imports `@intentius/chant-lexicon-k8s` and nothing installs it;
|
|
12
|
+
* - `linked` is member-commands' setup: a node_modules holding only the
|
|
13
|
+
* lexicon, linked to this checkout, and no `@intentius/chant`. chant loads
|
|
14
|
+
* the lexicon, and the lexicon resolves `@intentius/chant` from its own
|
|
15
|
+
* location, so it builds. The first attempt at #4116 (#4118) refused it.
|
|
16
|
+
*/
|
|
17
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
18
|
+
import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import { dirname, join } from "node:path";
|
|
21
|
+
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
|
22
|
+
|
|
23
|
+
const REPO = realpathSync(join(import.meta.dirname, "..", "..", "..", ".."));
|
|
24
|
+
const LOADER = join(REPO, "node_modules", "tsx", "dist", "loader.mjs");
|
|
25
|
+
const MAIN = join(REPO, "packages", "core", "src", "cli", "main.ts");
|
|
26
|
+
const TIMEOUT = 240_000;
|
|
27
|
+
|
|
28
|
+
let root: string;
|
|
29
|
+
|
|
30
|
+
function chant(args: string[], cwd: string): { status: number | null; stdout: string; stderr: string } {
|
|
31
|
+
const r = spawnSync(process.execPath, ["--import", `file://${LOADER}`, MAIN, ...args], {
|
|
32
|
+
cwd,
|
|
33
|
+
encoding: "utf-8",
|
|
34
|
+
timeout: TIMEOUT,
|
|
35
|
+
env: { ...process.env, TSX_DISABLE_CACHE: "1", NO_COLOR: "1" },
|
|
36
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
37
|
+
});
|
|
38
|
+
return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function project(name: string): string {
|
|
42
|
+
const dir = join(root, name);
|
|
43
|
+
const write = (path: string, text: string) => {
|
|
44
|
+
mkdirSync(dirname(join(dir, path)), { recursive: true });
|
|
45
|
+
writeFileSync(join(dir, path), text);
|
|
46
|
+
};
|
|
47
|
+
write("chant.config.ts", 'export default { lexicons: ["k8s"] };\n');
|
|
48
|
+
write(
|
|
49
|
+
"src/infra.ts",
|
|
50
|
+
'import { Namespace } from "@intentius/chant-lexicon-k8s";\nexport const appNs = new Namespace({ metadata: { name: "app" } });\n',
|
|
51
|
+
);
|
|
52
|
+
write(".gitignore", "node_modules\nout\n");
|
|
53
|
+
const git = (...args: string[]) =>
|
|
54
|
+
execFileSync("git", ["-c", "user.name=t", "-c", "user.email=t@example.com", "-c", "commit.gpgsign=false", ...args], { cwd: dir, stdio: "ignore" });
|
|
55
|
+
git("init", "-q");
|
|
56
|
+
git("add", "-A");
|
|
57
|
+
git("commit", "-qm", "fixture");
|
|
58
|
+
return dir;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let bare: string;
|
|
62
|
+
let linked: string;
|
|
63
|
+
|
|
64
|
+
beforeAll(() => {
|
|
65
|
+
root = realpathSync(mkdtempSync(join(tmpdir(), "chant-4116-e2e-")));
|
|
66
|
+
bare = project("bare");
|
|
67
|
+
linked = project("linked");
|
|
68
|
+
mkdirSync(join(linked, "node_modules", "@intentius"), { recursive: true });
|
|
69
|
+
symlinkSync(join(REPO, "lexicons", "k8s"), join(linked, "node_modules", "@intentius", "chant-lexicon-k8s"), "dir");
|
|
70
|
+
}, TIMEOUT);
|
|
71
|
+
|
|
72
|
+
afterAll(() => {
|
|
73
|
+
if (root) rmSync(root, { recursive: true, force: true });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("dependencies not installed (#4116)", () => {
|
|
77
|
+
test("graph says the dependencies are not installed, not that the source has lint errors", () => {
|
|
78
|
+
const r = chant(["graph", "--format", "ir"], bare);
|
|
79
|
+
expect(r.status, r.stderr).toBe(1);
|
|
80
|
+
expect(r.stderr).toContain(`dependencies not installed: @intentius/chant-lexicon-k8s does not resolve from ${bare}`);
|
|
81
|
+
expect(r.stderr).toContain("run npm install (or the project's package manager)");
|
|
82
|
+
expect(r.stderr).not.toMatch(/lint errors/i);
|
|
83
|
+
expect(r.stdout).toBe("");
|
|
84
|
+
}, TIMEOUT);
|
|
85
|
+
|
|
86
|
+
test("build and lint lead with the dependencies message and exit 1", () => {
|
|
87
|
+
for (const verb of ["build", "lint"]) {
|
|
88
|
+
const r = chant([verb], bare);
|
|
89
|
+
expect(r.status, `${verb}: ${r.stderr}`).toBe(1);
|
|
90
|
+
expect(r.stderr, verb).toContain("dependencies not installed: @intentius/chant-lexicon-k8s does not resolve from");
|
|
91
|
+
}
|
|
92
|
+
}, TIMEOUT);
|
|
93
|
+
|
|
94
|
+
test("a project without @intentius/chant in node_modules that builds today still builds", () => {
|
|
95
|
+
for (const args of [["build"], ["lint"], ["graph", "--format", "ir"]]) {
|
|
96
|
+
const r = chant(args, linked);
|
|
97
|
+
expect(r.status, `${args.join(" ")}: ${r.stderr}`).toBe(0);
|
|
98
|
+
expect(r.stderr, args.join(" ")).not.toContain("dependencies not installed");
|
|
99
|
+
}
|
|
100
|
+
}, TIMEOUT);
|
|
101
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chant #4116: which failure messages are imports of packages the project has
|
|
3
|
+
* not installed. The command paths that print the message have their own tests
|
|
4
|
+
* (handlers/graph.test.ts, handlers/lint.test.ts, dependencies.e2e.test.ts).
|
|
5
|
+
*/
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
7
|
+
import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { reportUninstalledDependencies, uninstalledDependencyIn } from "./dependencies";
|
|
11
|
+
|
|
12
|
+
let root: string;
|
|
13
|
+
let project: string;
|
|
14
|
+
|
|
15
|
+
const notFound = (pkg: string, file = "src/a.ts") => `Cannot find package '${pkg}' imported from ${join(project, file)}`;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
root = realpathSync(mkdtempSync(join(tmpdir(), "chant-4116-")));
|
|
19
|
+
project = join(root, "proj");
|
|
20
|
+
mkdirSync(join(project, "src"), { recursive: true });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
vi.restoreAllMocks();
|
|
25
|
+
rmSync(root, { recursive: true, force: true });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("uninstalledDependencyIn", () => {
|
|
29
|
+
test("an @intentius package no node_modules holds, as lint and build report it", () => {
|
|
30
|
+
expect(uninstalledDependencyIn(notFound("@intentius/chant-lexicon-aws"), project)).toEqual({
|
|
31
|
+
package: "@intentius/chant-lexicon-aws",
|
|
32
|
+
dir: project,
|
|
33
|
+
});
|
|
34
|
+
expect(uninstalledDependencyIn(`Component discovery error: ${notFound("@intentius/chant", "a.component.ts")}`, project)).toEqual({
|
|
35
|
+
package: "@intentius/chant",
|
|
36
|
+
dir: project,
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("a subpath import names its package", () => {
|
|
41
|
+
expect(uninstalledDependencyIn("Cannot find module '@intentius/chant/params' imported from /x/a.ts", project)?.package).toBe("@intentius/chant");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("a package found in a parent node_modules is installed", () => {
|
|
45
|
+
mkdirSync(join(root, "node_modules", "@intentius", "chant-lexicon-aws"), { recursive: true });
|
|
46
|
+
expect(uninstalledDependencyIn(notFound("@intentius/chant-lexicon-aws"), project)).toBeUndefined();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("a package found from the importing file's directory is installed", () => {
|
|
50
|
+
const lexicon = join(root, "elsewhere", "lexicon");
|
|
51
|
+
mkdirSync(join(root, "elsewhere", "node_modules", "@intentius", "chant"), { recursive: true });
|
|
52
|
+
mkdirSync(lexicon, { recursive: true });
|
|
53
|
+
expect(uninstalledDependencyIn(`Cannot find package '@intentius/chant' imported from ${join(lexicon, "index.ts")}`, project)).toBeUndefined();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("another package counts only when the project's package.json declares it", () => {
|
|
57
|
+
expect(uninstalledDependencyIn(notFound("lodahs"), project)).toBeUndefined();
|
|
58
|
+
writeFileSync(join(project, "package.json"), JSON.stringify({ dependencies: { yaml: "*" } }));
|
|
59
|
+
expect(uninstalledDependencyIn(notFound("yaml"), project)).toEqual({ package: "yaml", dir: project });
|
|
60
|
+
expect(uninstalledDependencyIn(notFound("lodahs"), project)).toBeUndefined();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("relative imports and other messages are not dependencies", () => {
|
|
64
|
+
expect(uninstalledDependencyIn("Cannot find module './missing' imported from /x/a.ts", project)).toBeUndefined();
|
|
65
|
+
expect(uninstalledDependencyIn("Exported declarable 'x' is never referenced", project)).toBeUndefined();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("reportUninstalledDependencies", () => {
|
|
70
|
+
let stderr: string[];
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
stderr = [];
|
|
73
|
+
vi.spyOn(console, "error").mockImplementation((s: string) => void stderr.push(s));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("names the first uninstalled package with the install hint; all when every message is one", () => {
|
|
77
|
+
expect(reportUninstalledDependencies([notFound("@intentius/chant-lexicon-aws"), notFound("@intentius/chant")], project)).toBe("all");
|
|
78
|
+
expect(stderr).toHaveLength(1);
|
|
79
|
+
expect(stderr[0]).toContain(`dependencies not installed: @intentius/chant-lexicon-aws does not resolve from ${project}`);
|
|
80
|
+
expect(stderr[0]).toContain("run npm install (or the project's package manager)");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("some when other errors are beside them", () => {
|
|
84
|
+
expect(reportUninstalledDependencies([notFound("@intentius/chant"), "a real problem"], project)).toBe("some");
|
|
85
|
+
expect(stderr).toHaveLength(1);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("prints nothing when no message is one", () => {
|
|
89
|
+
expect(reportUninstalledDependencies(["a real problem"], project)).toBeUndefined();
|
|
90
|
+
expect(reportUninstalledDependencies([], project)).toBeUndefined();
|
|
91
|
+
expect(stderr).toEqual([]);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chant #4116: a failure caused by a project whose dependencies are not installed.
|
|
3
|
+
*
|
|
4
|
+
* In a clone with no node_modules, every project file that imports
|
|
5
|
+
* `@intentius/chant` or a lexicon package fails to load. Lint reports each one
|
|
6
|
+
* (LEX002, COMP000), the graph's lint gate then says the source has lint
|
|
7
|
+
* errors, and build prints node's own "Cannot find package" per file. The
|
|
8
|
+
* source is fine; the install is missing.
|
|
9
|
+
*
|
|
10
|
+
* This runs only after something has already failed, on that failure's
|
|
11
|
+
* messages. A project chant builds today never reaches it, whatever its
|
|
12
|
+
* node_modules looks like: chant resolves a lexicon from its own install first,
|
|
13
|
+
* and a project that imports only what it has installed loads fine. That is why
|
|
14
|
+
* the check is not made up front (#4118, reverted in #4125).
|
|
15
|
+
*
|
|
16
|
+
* A message counts when it is node's "Cannot find package|module '<spec>'",
|
|
17
|
+
* `<spec>` names a package that is `@intentius/*` or declared in the project's
|
|
18
|
+
* package.json, and no node_modules from the project directory up holds it. A
|
|
19
|
+
* typo in an import of an undeclared package keeps its message as it is.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
22
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
import { packageNameOf } from "../lexicon-module";
|
|
25
|
+
import { formatError } from "./format";
|
|
26
|
+
|
|
27
|
+
/** A package that a failure could not import and that is not installed for the project. */
|
|
28
|
+
export interface UninstalledDependency {
|
|
29
|
+
package: string;
|
|
30
|
+
/** The project directory it does not resolve from. */
|
|
31
|
+
dir: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const NOT_FOUND = /Cannot find (?:package|module) '([^']+)'(?: imported from (\S+))?/g;
|
|
35
|
+
|
|
36
|
+
/** True when a node_modules at `dir` or above holds `pkg`. */
|
|
37
|
+
function installedFrom(pkg: string, dir: string): boolean {
|
|
38
|
+
for (let d = resolve(dir); ; d = dirname(d)) {
|
|
39
|
+
if (existsSync(join(d, "node_modules", ...pkg.split("/")))) return true;
|
|
40
|
+
if (dirname(d) === d) return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The packages the nearest package.json at `dir` or above declares, in any dependency field. */
|
|
45
|
+
function declaredPackages(dir: string): Set<string> {
|
|
46
|
+
for (let d = resolve(dir); ; d = dirname(d)) {
|
|
47
|
+
const file = join(d, "package.json");
|
|
48
|
+
if (existsSync(file)) {
|
|
49
|
+
try {
|
|
50
|
+
const json = JSON.parse(readFileSync(file, "utf-8")) as Record<string, unknown>;
|
|
51
|
+
const names = new Set<string>();
|
|
52
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
53
|
+
const deps = json[field];
|
|
54
|
+
if (deps && typeof deps === "object") for (const name of Object.keys(deps)) names.add(name);
|
|
55
|
+
}
|
|
56
|
+
return names;
|
|
57
|
+
} catch {
|
|
58
|
+
return new Set();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (dirname(d) === d) return new Set();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function importerDir(importer: string | undefined): string | undefined {
|
|
66
|
+
if (!importer) return undefined;
|
|
67
|
+
try {
|
|
68
|
+
const path = importer.startsWith("file:") ? fileURLToPath(importer) : importer;
|
|
69
|
+
return isAbsolute(path) ? dirname(path) : undefined;
|
|
70
|
+
} catch {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The uninstalled package `message` reports, or `undefined` when it reports none. */
|
|
76
|
+
export function uninstalledDependencyIn(message: string, projectDir: string): UninstalledDependency | undefined {
|
|
77
|
+
const dir = resolve(projectDir);
|
|
78
|
+
let declared: Set<string> | undefined;
|
|
79
|
+
for (const match of message.matchAll(NOT_FOUND)) {
|
|
80
|
+
const pkg = packageNameOf(match[1]);
|
|
81
|
+
if (pkg === undefined) continue;
|
|
82
|
+
if (!pkg.startsWith("@intentius/")) {
|
|
83
|
+
declared ??= declaredPackages(dir);
|
|
84
|
+
if (!declared.has(pkg)) continue;
|
|
85
|
+
}
|
|
86
|
+
if (installedFrom(pkg, dir)) continue;
|
|
87
|
+
const from = importerDir(match[2]);
|
|
88
|
+
if (from !== undefined && installedFrom(pkg, from)) continue;
|
|
89
|
+
return { package: pkg, dir };
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The dependencies message, formatted as an error with the install hint. */
|
|
95
|
+
export function formatDependenciesNotInstalled(dep: UninstalledDependency): string {
|
|
96
|
+
return formatError({
|
|
97
|
+
message: `dependencies not installed: ${dep.package} does not resolve from ${dep.dir}`,
|
|
98
|
+
hint: "run npm install (or the project's package manager)",
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Print the dependencies message, naming the first uninstalled package, when
|
|
104
|
+
* any of a failure's `messages` reports one. Prints nothing and returns
|
|
105
|
+
* `undefined` when none does. Returns `"all"` when every message reports one,
|
|
106
|
+
* so a caller can drop its own wording when that wording blames the source.
|
|
107
|
+
*/
|
|
108
|
+
export function reportUninstalledDependencies(
|
|
109
|
+
messages: readonly string[],
|
|
110
|
+
projectDir: string,
|
|
111
|
+
): "all" | "some" | undefined {
|
|
112
|
+
let first: UninstalledDependency | undefined;
|
|
113
|
+
let all = messages.length > 0;
|
|
114
|
+
for (const message of messages) {
|
|
115
|
+
const dep = uninstalledDependencyIn(message, projectDir);
|
|
116
|
+
if (dep) first ??= dep;
|
|
117
|
+
else all = false;
|
|
118
|
+
}
|
|
119
|
+
if (!first) return undefined;
|
|
120
|
+
console.error(formatDependenciesNotInstalled(first));
|
|
121
|
+
return all ? "all" : "some";
|
|
122
|
+
}
|
|
@@ -148,6 +148,111 @@ describe("runBuild --components --generate (chant #1108 build-time parameters)",
|
|
|
148
148
|
vi.restoreAllMocks();
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
+
test("--pr-loop asks for --require-review only under --require-review or the review-rules security group (#4168)", async () => {
|
|
152
|
+
const { mkdtempSync, mkdirSync, writeFileSync, rmSync } = await import("node:fs");
|
|
153
|
+
const { tmpdir } = await import("node:os");
|
|
154
|
+
const { join } = await import("node:path");
|
|
155
|
+
const { execFileSync } = await import("node:child_process");
|
|
156
|
+
const root = mkdtempSync(join(tmpdir(), "chant-build-review-rules-"));
|
|
157
|
+
try {
|
|
158
|
+
execFileSync("git", ["init", "-q"], { cwd: root });
|
|
159
|
+
mkdirSync(join(root, "app"));
|
|
160
|
+
const declare = (extra: Record<string, unknown>) =>
|
|
161
|
+
writeFileSync(join(root, "chant.workspace.json"), JSON.stringify({ name: "acme", schema: 1, ...extra, members: [{ name: "app", dir: "app", kind: "chant", because: "the app" }] }));
|
|
162
|
+
generateComponentsPipelineMock.mockResolvedValue({ success: true, yaml: "jobs: {}", stages: [], jobs: [] });
|
|
163
|
+
vi.spyOn(console, "log").mockImplementation(() => {});
|
|
164
|
+
const path = join(root, "app");
|
|
165
|
+
const output = join(root, "out.yml");
|
|
166
|
+
|
|
167
|
+
declare({});
|
|
168
|
+
expect(await runBuild({ args: makeArgs({ path, output, generate: "github", prLoop: true }), plugins: [], serializers: [] })).toBe(0);
|
|
169
|
+
expect(generateComponentsPipelineMock.mock.calls[0][2].prLoop).toEqual({});
|
|
170
|
+
expect(await runBuild({ args: makeArgs({ path, output, generate: "github", prLoop: true, requireReview: true }), plugins: [], serializers: [] })).toBe(0);
|
|
171
|
+
expect(generateComponentsPipelineMock.mock.calls[1][2].prLoop).toEqual({ requireReview: true });
|
|
172
|
+
|
|
173
|
+
declare({ security: ["review-rules"] });
|
|
174
|
+
expect(await runBuild({ args: makeArgs({ path, output, generate: "github", prLoop: true }), plugins: [], serializers: [] })).toBe(0);
|
|
175
|
+
expect(generateComponentsPipelineMock.mock.calls[2][2].prLoop).toEqual({ requireReview: true });
|
|
176
|
+
|
|
177
|
+
// #4168 step 7: a member's own group counts, and isolated-runners asks for --require-runner-start.
|
|
178
|
+
writeFileSync(join(root, "chant.workspace.json"), JSON.stringify({ name: "acme", schema: 1, members: [{ name: "app", dir: "app", kind: "chant", because: "the app", security: ["isolated-runners"] }] }));
|
|
179
|
+
expect(await runBuild({ args: makeArgs({ path, output, generate: "github", prLoop: true }), plugins: [], serializers: [] })).toBe(0);
|
|
180
|
+
expect(generateComponentsPipelineMock.mock.calls[3][2].prLoop).toEqual({ requireRunnerStart: true });
|
|
181
|
+
} finally {
|
|
182
|
+
rmSync(root, { recursive: true, force: true });
|
|
183
|
+
vi.restoreAllMocks();
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("--pr-loop without --report-url bakes the workspace's ci.reportUrl into the jobs, and the flag wins (#4160)", async () => {
|
|
188
|
+
const { mkdtempSync, mkdirSync, writeFileSync, rmSync } = await import("node:fs");
|
|
189
|
+
const { tmpdir } = await import("node:os");
|
|
190
|
+
const { join } = await import("node:path");
|
|
191
|
+
const { execFileSync } = await import("node:child_process");
|
|
192
|
+
const root = mkdtempSync(join(tmpdir(), "chant-build-report-url-"));
|
|
193
|
+
try {
|
|
194
|
+
execFileSync("git", ["init", "-q"], { cwd: root });
|
|
195
|
+
mkdirSync(join(root, "app"));
|
|
196
|
+
writeFileSync(
|
|
197
|
+
join(root, "chant.workspace.json"),
|
|
198
|
+
JSON.stringify({ name: "acme", schema: 1, ci: { reportUrl: "s3://acme-reports/prod/" }, members: [{ name: "app", dir: "app", kind: "chant", because: "the app" }] }),
|
|
199
|
+
);
|
|
200
|
+
generateComponentsPipelineMock.mockResolvedValue({ success: true, yaml: "jobs: {}", stages: [], jobs: [] });
|
|
201
|
+
vi.spyOn(console, "log").mockImplementation(() => {});
|
|
202
|
+
const path = join(root, "app");
|
|
203
|
+
const output = join(root, "out.yml");
|
|
204
|
+
|
|
205
|
+
expect(await runBuild({ args: makeArgs({ path, output, generate: "github", prLoop: true }), plugins: [], serializers: [] })).toBe(0);
|
|
206
|
+
expect(generateComponentsPipelineMock.mock.calls[0][2].prLoop).toEqual({ reportUrl: "s3://acme-reports/prod" });
|
|
207
|
+
|
|
208
|
+
expect(await runBuild({ args: makeArgs({ path, output, generate: "github", prLoop: true, reportUrl: "file:///srv/reports" }), plugins: [], serializers: [] })).toBe(0);
|
|
209
|
+
expect(generateComponentsPipelineMock.mock.calls[1][2].prLoop).toEqual({ reportUrl: "file:///srv/reports" });
|
|
210
|
+
|
|
211
|
+
// Without --pr-loop the declaration's URL is not read and nothing changes.
|
|
212
|
+
expect(await runBuild({ args: makeArgs({ path, output, generate: "github" }), plugins: [], serializers: [] })).toBe(0);
|
|
213
|
+
expect(generateComponentsPipelineMock.mock.calls[2][2]).not.toHaveProperty("prLoop");
|
|
214
|
+
} finally {
|
|
215
|
+
rmSync(root, { recursive: true, force: true });
|
|
216
|
+
vi.restoreAllMocks();
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("--pr-loop outside any workspace has no report URL unless the flag gives one (#4160)", async () => {
|
|
221
|
+
const { mkdtempSync, rmSync } = await import("node:fs");
|
|
222
|
+
const { tmpdir } = await import("node:os");
|
|
223
|
+
const { join } = await import("node:path");
|
|
224
|
+
const plain = mkdtempSync(join(tmpdir(), "chant-build-plain-"));
|
|
225
|
+
try {
|
|
226
|
+
generateComponentsPipelineMock.mockResolvedValue({ success: true, yaml: "jobs: {}", stages: [], jobs: [] });
|
|
227
|
+
vi.spyOn(console, "log").mockImplementation(() => {});
|
|
228
|
+
expect(await runBuild({ args: makeArgs({ path: plain, generate: "github", prLoop: true }), plugins: [], serializers: [] })).toBe(0);
|
|
229
|
+
expect(generateComponentsPipelineMock).toHaveBeenCalledWith(plain, "github", { env: undefined, prLoop: {} }, undefined, []);
|
|
230
|
+
} finally {
|
|
231
|
+
rmSync(plain, { recursive: true, force: true });
|
|
232
|
+
vi.restoreAllMocks();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("an invalid ci.reportUrl stops --pr-loop with its reason code, and generates nothing (#4160)", async () => {
|
|
237
|
+
const { mkdtempSync, mkdirSync, writeFileSync, rmSync } = await import("node:fs");
|
|
238
|
+
const { tmpdir } = await import("node:os");
|
|
239
|
+
const { join } = await import("node:path");
|
|
240
|
+
const { execFileSync } = await import("node:child_process");
|
|
241
|
+
const root = mkdtempSync(join(tmpdir(), "chant-build-report-url-bad-"));
|
|
242
|
+
try {
|
|
243
|
+
execFileSync("git", ["init", "-q"], { cwd: root });
|
|
244
|
+
mkdirSync(join(root, "app"));
|
|
245
|
+
writeFileSync(join(root, "chant.workspace.json"), JSON.stringify({ name: "acme", schema: 1, ci: { reportUrl: "https://reports.example" }, members: [{ name: "app", dir: "app", kind: "chant", because: "the app" }] }));
|
|
246
|
+
const stderr = makeStderrSpy();
|
|
247
|
+
expect(await runBuild({ args: makeArgs({ path: join(root, "app"), generate: "github", prLoop: true }), plugins: [], serializers: [] })).toBe(1);
|
|
248
|
+
expect(generateComponentsPipelineMock).not.toHaveBeenCalled();
|
|
249
|
+
expect(stderr.join("\n")).toMatch(/ci\.reportUrl https:\/\/reports\.example: .*\[declaration-invalid\]/);
|
|
250
|
+
} finally {
|
|
251
|
+
rmSync(root, { recursive: true, force: true });
|
|
252
|
+
vi.restoreAllMocks();
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
151
256
|
test("--pr-build and --pr-build-command become the prLoop build options (#4088)", async () => {
|
|
152
257
|
generateComponentsPipelineMock.mockResolvedValue({ success: true, yaml: "jobs: {}", stages: [], jobs: [] });
|
|
153
258
|
vi.spyOn(console, "log").mockImplementation(() => {});
|
|
@@ -2,6 +2,7 @@ import { writeFileSync, mkdirSync } from "node:fs";
|
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { buildCommand, buildCommandWatch, printErrors, printWarnings, resolveBuildFormat } from "../commands/build";
|
|
4
4
|
import { formatError, formatInfo, formatSuccess, formatBold } from "../format";
|
|
5
|
+
import { reportUninstalledDependencies } from "../dependencies";
|
|
5
6
|
import type { CommandContext } from "../registry";
|
|
6
7
|
import { generateComponentsPipeline } from "../../components/cli-support";
|
|
7
8
|
import { loadChantConfigUpward, type ChantConfig } from "../../config";
|
|
@@ -15,15 +16,23 @@ import type { PrLoopPipelineOptions } from "../../components/pr-pipeline";
|
|
|
15
16
|
* and the branch, and with `--events-json` and `--report-url` (#4007) the
|
|
16
17
|
* flags the generated jobs pass to pr-plan and pr-apply. `--pr-build` and
|
|
17
18
|
* `--pr-build-command` (#4088) add the build and lint steps before both.
|
|
19
|
+
* `reportUrl` is `--report-url`, else the workspace's `ci.reportUrl` (#4160).
|
|
20
|
+
* `requireReview` is `--require-review`, or a workspace that opts into the
|
|
21
|
+
* `review-rules` security group (#4168); off otherwise. `requireRunnerStart`
|
|
22
|
+
* is `--require-runner-start`, or the `isolated-runners` group. `--apply-on-merge`
|
|
23
|
+
* (#4168) writes the apply job on merge in place of the record job.
|
|
18
24
|
*/
|
|
19
|
-
function prLoopOption(args: CommandContext["args"]): PrLoopPipelineOptions {
|
|
25
|
+
function prLoopOption(args: CommandContext["args"], reportUrl: string | undefined, requireReview = false, requireRunnerStart = false): PrLoopPipelineOptions {
|
|
20
26
|
return {
|
|
21
27
|
...(args.gate ? { gate: args.gate } : {}),
|
|
22
28
|
...(args.branch ? { branch: args.branch } : {}),
|
|
23
29
|
...(args.eventsJson ? { events: true } : {}),
|
|
24
|
-
...(
|
|
30
|
+
...(reportUrl ? { reportUrl } : {}),
|
|
25
31
|
...(args.prBuild ? { build: true } : {}),
|
|
26
32
|
...(args.prBuildCommand ? { buildCommand: args.prBuildCommand } : {}),
|
|
33
|
+
...(args.requireReview === true || requireReview ? { requireReview: true } : {}),
|
|
34
|
+
...(args.requireRunnerStart === true || requireRunnerStart ? { requireRunnerStart: true } : {}),
|
|
35
|
+
...(args.applyOnMerge ? { applyOnMerge: true } : {}),
|
|
27
36
|
};
|
|
28
37
|
}
|
|
29
38
|
|
|
@@ -90,6 +99,24 @@ async function runGenerateComponents(ctx: CommandContext): Promise<number> {
|
|
|
90
99
|
const found = findWorkspaceRoot(resolve(args.path));
|
|
91
100
|
let plan: import("../../workspace/member-pipeline").MemberComponentPlan | undefined;
|
|
92
101
|
let memberPipeline: typeof import("../../workspace/member-pipeline") | undefined;
|
|
102
|
+
// #4160: without --report-url, the declaration's ci.reportUrl. Read only inside a workspace.
|
|
103
|
+
let reportUrl = args.reportUrl;
|
|
104
|
+
if (found && args.prLoop && !reportUrl) {
|
|
105
|
+
try {
|
|
106
|
+
reportUrl = await (await import("../../workspace/ci-report-url")).declaredReportUrl(resolve(args.path));
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.error(formatError({ message: err instanceof Error ? err.message : String(err) }));
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// #4168: the review-rules group turns on --require-review in the generated apply. Read only inside a workspace.
|
|
113
|
+
let reviewRules = false;
|
|
114
|
+
let isolatedRunners = false;
|
|
115
|
+
if (found && args.prLoop) {
|
|
116
|
+
const groups = (await import("../../workspace/security")).securityGroupsAt(resolve(args.path));
|
|
117
|
+
reviewRules = groups.has("review-rules");
|
|
118
|
+
isolatedRunners = groups.has("isolated-runners");
|
|
119
|
+
}
|
|
93
120
|
if (found) {
|
|
94
121
|
memberPipeline = await import("../../workspace/member-pipeline");
|
|
95
122
|
try {
|
|
@@ -104,7 +131,7 @@ async function runGenerateComponents(ctx: CommandContext): Promise<number> {
|
|
|
104
131
|
params: args.param,
|
|
105
132
|
paramsFile: args.paramsFile,
|
|
106
133
|
...(args.waveGate ? { waveGate: { gate: args.waveGate, ...(args.canary?.length ? { canary: args.canary } : {}), ...(args.base ? { base: args.base } : {}) } } : {}),
|
|
107
|
-
...(args.prLoop ? { prLoop: prLoopOption(args) } : {}),
|
|
134
|
+
...(args.prLoop ? { prLoop: prLoopOption(args, reportUrl, reviewRules, isolatedRunners) } : {}),
|
|
108
135
|
...(args.image ? { image: args.image } : {}),
|
|
109
136
|
...(args.beforeScript?.length ? { beforeScript: args.beforeScript } : {}),
|
|
110
137
|
});
|
|
@@ -134,7 +161,7 @@ async function runGenerateComponents(ctx: CommandContext): Promise<number> {
|
|
|
134
161
|
? { gatedWaves: { gate: args.waveGate, ...(args.canary?.length ? { canary: args.canary } : {}), ...(args.base ? { base: args.base } : {}) } }
|
|
135
162
|
: {}),
|
|
136
163
|
// #3183: a plan job per pull request and an apply job per merge.
|
|
137
|
-
...(args.prLoop ? { prLoop: prLoopOption(args) } : {}),
|
|
164
|
+
...(args.prLoop ? { prLoop: prLoopOption(args, reportUrl, reviewRules, isolatedRunners) } : {}),
|
|
138
165
|
// #4002: the job image and the lines that install what it lacks, chant itself included.
|
|
139
166
|
...(args.image ? { image: args.image } : {}),
|
|
140
167
|
...(args.beforeScript?.length ? { beforeScript: args.beforeScript } : {}),
|
|
@@ -284,6 +311,8 @@ export async function runBuild(ctx: CommandContext): Promise<number> {
|
|
|
284
311
|
const own = result.warnings.filter((w) => !w.includes("No serializer found for lexicon") && !seenWarnings.has(w));
|
|
285
312
|
for (const w of own) seenWarnings.add(w);
|
|
286
313
|
printWarnings(own);
|
|
314
|
+
// #4116: once, before the first failing group's errors.
|
|
315
|
+
if (!result.success && !failed) reportUninstalledDependencies(result.errors, resolve(args.path));
|
|
287
316
|
printErrors(result.errors);
|
|
288
317
|
if (!result.success) failed = true;
|
|
289
318
|
}
|
|
@@ -333,6 +362,8 @@ export async function runBuild(ctx: CommandContext): Promise<number> {
|
|
|
333
362
|
warnings = warnings.filter((w) => !w.includes('No serializer found for lexicon'));
|
|
334
363
|
}
|
|
335
364
|
printWarnings(warnings);
|
|
365
|
+
// #4116: errors that are imports of uninstalled packages are led by the dependencies message.
|
|
366
|
+
if (!result.success) reportUninstalledDependencies(result.errors, resolve(args.path));
|
|
336
367
|
printErrors(result.errors);
|
|
337
368
|
|
|
338
369
|
return result.success ? 0 : 1;
|
|
@@ -78,6 +78,18 @@ const ESTATE = [
|
|
|
78
78
|
|
|
79
79
|
let ran: string[] = [];
|
|
80
80
|
let failing: string[] = [];
|
|
81
|
+
/** The error a component's step throws, in place of applying (#3997). */
|
|
82
|
+
let refusal: Record<string, Error> = {};
|
|
83
|
+
|
|
84
|
+
/** A step's refusal of a plan that moved since it was approved, marked `planMoved` as cfn-deploy marks it. */
|
|
85
|
+
function planMovedError(component: string): Error {
|
|
86
|
+
return Object.assign(new Error(`${component}: refusing to apply, the plan moved`), { planMoved: true });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** A step error carrying an `exitCode`, as kubectl-apply's carried one before #3997's follow-up. */
|
|
90
|
+
function exitCodeError(component: string, exitCode: number): Error {
|
|
91
|
+
return Object.assign(new Error(`${component}: refused`), { exitCode });
|
|
92
|
+
}
|
|
81
93
|
/** What each component's step was handed, after wiring resolved. */
|
|
82
94
|
let inputs: Record<string, Record<string, unknown>> = {};
|
|
83
95
|
/** Stack outputs a component's step returns, as cfn-deploy's `outputs` does. */
|
|
@@ -118,6 +130,7 @@ beforeEach(() => {
|
|
|
118
130
|
vi.clearAllMocks();
|
|
119
131
|
ran = [];
|
|
120
132
|
failing = [];
|
|
133
|
+
refusal = {};
|
|
121
134
|
inputs = {};
|
|
122
135
|
stepOutputs = {};
|
|
123
136
|
onStart = () => {};
|
|
@@ -155,6 +168,7 @@ beforeEach(() => {
|
|
|
155
168
|
onStart(runCtx.component);
|
|
156
169
|
ran.push(runCtx.component);
|
|
157
170
|
inputs[runCtx.component] = input;
|
|
171
|
+
if (refusal[runCtx.component]) throw refusal[runCtx.component];
|
|
158
172
|
if (failing.includes(runCtx.component)) throw new Error(`${runCtx.component} failed`);
|
|
159
173
|
return { ok: true, ...(stepOutputs[runCtx.component] ? { outputs: stepOutputs[runCtx.component] } : {}) };
|
|
160
174
|
},
|
|
@@ -310,6 +324,31 @@ describe("proof: a mid-run failure blocks its subtree and leaves the independent
|
|
|
310
324
|
});
|
|
311
325
|
});
|
|
312
326
|
|
|
327
|
+
describe("a step that refuses its plan because the plan moved exits 3 (#3997)", () => {
|
|
328
|
+
test("marked planMoved: exit 3, and its subtree is blocked as for any failure", async () => {
|
|
329
|
+
refusal = { "cluster-a": planMovedError("cluster-a") };
|
|
330
|
+
expect(await runComponentsFanOut(ctx({ base: "main", env: "test" }))).toBe(3);
|
|
331
|
+
expect(ran).not.toContain("app-one");
|
|
332
|
+
expect(ran).toContain("app-three");
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test("an exitCode of 3 on the step's error is the same mark", async () => {
|
|
336
|
+
refusal = { "cluster-a": exitCodeError("cluster-a", 3) };
|
|
337
|
+
expect(await runComponentsFanOut(ctx({ base: "main", env: "test" }))).toBe(3);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test("any other exitCode is ignored, and the failure exits 1", async () => {
|
|
341
|
+
refusal = { "cluster-a": exitCodeError("cluster-a", 7) };
|
|
342
|
+
expect(await runComponentsFanOut(ctx({ base: "main", env: "test" }))).toBe(1);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test("one moved plan beside another failure still exits 3", async () => {
|
|
346
|
+
refusal = { "cluster-a": planMovedError("cluster-a") };
|
|
347
|
+
failing = ["cluster-b"];
|
|
348
|
+
expect(await runComponentsFanOut(ctx({ base: "main", env: "test" }))).toBe(3);
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
313
352
|
describe("proof: the re-run prints already-applied for what it skips, and finishes", () => {
|
|
314
353
|
test("repeating the identical command finishes the fan-out, and the approval is not re-asked", async () => {
|
|
315
354
|
const dir = await mkdtemp(join(tmpdir(), "fan-out-"));
|
|
@@ -523,6 +562,7 @@ describe("gated waves (#3049): one gate per wave, bound to the wave's set digest
|
|
|
523
562
|
},
|
|
524
563
|
async run(runCtx: { component: string }) {
|
|
525
564
|
ran.push(runCtx.component);
|
|
565
|
+
if (refusal[runCtx.component]) throw refusal[runCtx.component];
|
|
526
566
|
return { ok: true };
|
|
527
567
|
},
|
|
528
568
|
} as never);
|
|
@@ -530,6 +570,39 @@ describe("gated waves (#3049): one gate per wave, bound to the wave's set digest
|
|
|
530
570
|
});
|
|
531
571
|
});
|
|
532
572
|
|
|
573
|
+
/** Run the gated fan-out once to record wave 1's pending fact, then approve that digest. */
|
|
574
|
+
async function approveWave1(run: () => Promise<number>): Promise<void> {
|
|
575
|
+
await run();
|
|
576
|
+
const first = gateLedger.appended[0];
|
|
577
|
+
gateLedger = memoryGateLedgerPort({
|
|
578
|
+
pending: [first],
|
|
579
|
+
resolutions: [
|
|
580
|
+
{
|
|
581
|
+
version: 1,
|
|
582
|
+
kind: "resolution",
|
|
583
|
+
op: "fan-out",
|
|
584
|
+
gate: "release-wave-1",
|
|
585
|
+
resolvedBy: "operator",
|
|
586
|
+
timestamp: new Date(Date.parse(first.timestamp) + 1000).toISOString(),
|
|
587
|
+
planDigest: first.planDigest,
|
|
588
|
+
},
|
|
589
|
+
],
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
test.each([
|
|
594
|
+
["marked planMoved", 3, () => planMovedError("net")],
|
|
595
|
+
["with exitCode 3", 3, () => exitCodeError("net", 3)],
|
|
596
|
+
["with exitCode 7", 1, () => exitCodeError("net", 7)],
|
|
597
|
+
["unmarked", 1, () => new Error("net failed")],
|
|
598
|
+
])("an approved wave whose step fails with an error %s exits %i (#3997)", async (_label, code, error) => {
|
|
599
|
+
const run = () => runComponentsFanOut(ctx({ base: "main", env: "test", waveGate: "release" }));
|
|
600
|
+
await approveWave1(run);
|
|
601
|
+
refusal = { net: error() };
|
|
602
|
+
expect(await run()).toBe(code);
|
|
603
|
+
expect(ran).toEqual(["net"]);
|
|
604
|
+
});
|
|
605
|
+
|
|
533
606
|
test("wave 1 stops at its own gate, and the record names its digest", async () => {
|
|
534
607
|
const dir = await mkdtemp(join(tmpdir(), "fan-out-waves-"));
|
|
535
608
|
try {
|