@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
|
@@ -104,6 +104,55 @@
|
|
|
104
104
|
}
|
|
105
105
|
]
|
|
106
106
|
},
|
|
107
|
+
"newer": {
|
|
108
|
+
"type": "array",
|
|
109
|
+
"description": "Added within contract version 1 by #4169, written by chant 0.124.33 and newer. The first-parent commits walked past, newest first and above the one found, each with what its _ci/<sha>.json record on the local chant/lifecycle says (null when it has none), so a reader can say why the newest commits are not green. At most 50.",
|
|
110
|
+
"items": {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"required": [
|
|
113
|
+
"sha",
|
|
114
|
+
"record"
|
|
115
|
+
],
|
|
116
|
+
"properties": {
|
|
117
|
+
"sha": {
|
|
118
|
+
"type": "string",
|
|
119
|
+
"pattern": "^[0-9a-f]{40,64}$"
|
|
120
|
+
},
|
|
121
|
+
"record": {
|
|
122
|
+
"oneOf": [
|
|
123
|
+
{
|
|
124
|
+
"type": "null"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"type": "object",
|
|
128
|
+
"required": [
|
|
129
|
+
"state"
|
|
130
|
+
],
|
|
131
|
+
"properties": {
|
|
132
|
+
"state": {
|
|
133
|
+
"enum": [
|
|
134
|
+
"green",
|
|
135
|
+
"red",
|
|
136
|
+
"unbuilt",
|
|
137
|
+
"pending"
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
"reason": {
|
|
141
|
+
"type": "string"
|
|
142
|
+
},
|
|
143
|
+
"coveredBy": {
|
|
144
|
+
"type": "string"
|
|
145
|
+
},
|
|
146
|
+
"culprit": {
|
|
147
|
+
"type": "string"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
107
156
|
"ignored": {
|
|
108
157
|
"type": "array",
|
|
109
158
|
"description": "Added within contract version 1 by #4031, written by chant 0.124.28 and newer. The tags on the commits walked, newest first and down to the one found, that do not count for want of a valid statement. Empty when ci.green.signedSince is not set.",
|
|
@@ -240,8 +240,11 @@
|
|
|
240
240
|
"description": "The env the run is recorded under."
|
|
241
241
|
},
|
|
242
242
|
"signer": {
|
|
243
|
-
"type":
|
|
244
|
-
|
|
243
|
+
"type": [
|
|
244
|
+
"string",
|
|
245
|
+
"null"
|
|
246
|
+
],
|
|
247
|
+
"description": "The runner principal whose statement the run carries. null for an unsigned run, which counts unless ci.green.signedSince asks for a statement (#4156, chant 0.124.32 and newer)."
|
|
245
248
|
}
|
|
246
249
|
},
|
|
247
250
|
"description": "Added within contract version 1 by #4044, written by chant 0.124.28 and newer. Present only when the run is a chant Op run (ci.green.source runs): name is then the Op's name and id is null. Absent for a check run."
|
|
@@ -281,9 +284,10 @@
|
|
|
281
284
|
"pass",
|
|
282
285
|
"fail",
|
|
283
286
|
"pending",
|
|
287
|
+
"unbuilt",
|
|
284
288
|
"unrecorded"
|
|
285
289
|
],
|
|
286
|
-
"description": "pass and fail come from the tags (a green tag records the phases that passed, a revoked tag the one that failed) or, for an undecided commit, from the forge under --forge, or from the signed Op runs on the local chant/lifecycle when the source is chant runs (#4044). pending is such a read with runs still going or not yet reported. unrecorded means no tag holds the result and nothing was read."
|
|
290
|
+
"description": "pass and fail come from the tags (a green tag records the phases that passed, a revoked tag the one that failed) or, for an undecided commit, from the forge under --forge, or from the signed Op runs on the local chant/lifecycle when the source is chant runs (#4044). pending is such a read with runs still going or not yet reported. unbuilt (#4169, chant 0.124.33 and newer) is such a read whose runs were cancelled. unrecorded means no tag holds the result and nothing was read."
|
|
287
291
|
},
|
|
288
292
|
"runs": {
|
|
289
293
|
"type": "array",
|
|
@@ -334,8 +338,11 @@
|
|
|
334
338
|
"description": "The env the run is recorded under."
|
|
335
339
|
},
|
|
336
340
|
"signer": {
|
|
337
|
-
"type":
|
|
338
|
-
|
|
341
|
+
"type": [
|
|
342
|
+
"string",
|
|
343
|
+
"null"
|
|
344
|
+
],
|
|
345
|
+
"description": "The runner principal whose statement the run carries. null for an unsigned run, which counts unless ci.green.signedSince asks for a statement (#4156, chant 0.124.32 and newer)."
|
|
339
346
|
}
|
|
340
347
|
},
|
|
341
348
|
"description": "Added within contract version 1 by #4044, written by chant 0.124.28 and newer. Present only when the run is a chant Op run (ci.green.source runs): name is then the Op's name and id is null. Absent for a check run."
|
|
@@ -400,6 +407,39 @@
|
|
|
400
407
|
}
|
|
401
408
|
]
|
|
402
409
|
},
|
|
410
|
+
"record": {
|
|
411
|
+
"description": "Added within contract version 1 by #4169, written by chant 0.124.33 and newer. What the commit's _ci/<sha>.json record on the local chant/lifecycle says, the tick's last word on it, or null when it has none. state is green, red, unbuilt or pending. reason, coveredBy and culprit come from the required phases: why a phase is unbuilt or still pending (cancelled, superseded, retrying, requested, request-unserved or aged-out), the newer commit whose run covers a superseded phase, and the commit a bisect found broke a phase. Each is absent when no phase has one.",
|
|
412
|
+
"oneOf": [
|
|
413
|
+
{
|
|
414
|
+
"type": "null"
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
"type": "object",
|
|
418
|
+
"required": [
|
|
419
|
+
"state"
|
|
420
|
+
],
|
|
421
|
+
"properties": {
|
|
422
|
+
"state": {
|
|
423
|
+
"enum": [
|
|
424
|
+
"green",
|
|
425
|
+
"red",
|
|
426
|
+
"unbuilt",
|
|
427
|
+
"pending"
|
|
428
|
+
]
|
|
429
|
+
},
|
|
430
|
+
"reason": {
|
|
431
|
+
"type": "string"
|
|
432
|
+
},
|
|
433
|
+
"coveredBy": {
|
|
434
|
+
"type": "string"
|
|
435
|
+
},
|
|
436
|
+
"culprit": {
|
|
437
|
+
"type": "string"
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
]
|
|
442
|
+
},
|
|
403
443
|
"ignored": {
|
|
404
444
|
"type": "array",
|
|
405
445
|
"description": "Added within contract version 1 by #4031, written by chant 0.124.28 and newer. The commit's ci tags that do not count for want of a valid statement signed by a runner key. A tag on any commit that is not a strict first-parent ancestor of ci.green.signedSince needs one; empty when signedSince is not set. state, green and revoked count only the tags that are not ignored.",
|
|
@@ -432,7 +472,7 @@
|
|
|
432
472
|
},
|
|
433
473
|
"ignoredRuns": {
|
|
434
474
|
"type": "array",
|
|
435
|
-
"description": "Added within contract version 1 by #4044, written by chant 0.124.28 and newer. Present only when the source is chant runs (ci.green.source or --source runs): the Op runs on the commit that do not count
|
|
475
|
+
"description": "Added within contract version 1 by #4044, written by chant 0.124.28 and newer. Present only when the source is chant runs (ci.green.source or --source runs): the Op runs on the commit that do not count: a statement that does not verify (invalid, untrusted or mismatch), or, under ci.green.signedSince, no statement on a commit that needs one (#4156). They neither green nor revoke the commit.",
|
|
436
476
|
"items": {
|
|
437
477
|
"type": "object",
|
|
438
478
|
"required": [
|
|
@@ -460,7 +500,7 @@
|
|
|
460
500
|
"untrusted",
|
|
461
501
|
"mismatch"
|
|
462
502
|
],
|
|
463
|
-
"description": "unsigned: the record carries no statement. invalid: the statement is not an op-run statement, or names another signer than the key's. untrusted: no runner key .chant/trust.json lists at base signed it, or its principal holds no runner role. mismatch: it signs another run, commit or record than the ledger holds."
|
|
503
|
+
"description": "unsigned: the record carries no statement, on a commit ci.green.signedSince asks one for (#4156: without signedSince an unsigned run counts). invalid: the statement is not an op-run statement, or names another signer than the key's. untrusted: no runner key .chant/trust.json lists at base signed it, or its principal holds no runner role. mismatch: it signs another run, commit or record than the ledger holds."
|
|
464
504
|
},
|
|
465
505
|
"reason": {
|
|
466
506
|
"type": "string"
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #4160: the report URL a workspace declares as `ci.reportUrl`. `chant ci
|
|
3
|
+
* run`, `ci serve` and `ci runs` use it when `--report-url` is not given, the
|
|
4
|
+
* flag wins, and `chant workspace ls --json` carries it for readers.
|
|
5
|
+
*
|
|
6
|
+
* The jobs are real `ci run` jobs in a temp repository with a stub chant and
|
|
7
|
+
* file:// report URLs in other temp directories. Nothing is pushed and no
|
|
8
|
+
* test makes a network call: `ci serve` is stopped by its first poll, which
|
|
9
|
+
* has no forge to reach.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { chmodSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { execFileSync } from "node:child_process";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { pathToFileURL } from "node:url";
|
|
16
|
+
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
|
|
17
|
+
import { cleanScratch, contract, declaration, git, repoFixture, scratchDir, writeFiles } from "./__fixtures__/contract-repo";
|
|
18
|
+
import { parseArgs } from "../cli/parse-args";
|
|
19
|
+
import type { CommandContext } from "../cli/registry";
|
|
20
|
+
import { declaredReportUrl, reportUrlOf } from "./ci-report-url";
|
|
21
|
+
import { runCiRun } from "./ci-run";
|
|
22
|
+
import { runCiServe } from "./ci-serve";
|
|
23
|
+
import { CI_RUN_INDEX_KEY, runCiRuns, type CiRunIndex } from "./ci-run-index";
|
|
24
|
+
import { listWorkspace, type LsDocument } from "./ls";
|
|
25
|
+
import lsSchema from "./ls.schema.json";
|
|
26
|
+
|
|
27
|
+
const home = process.cwd();
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
process.chdir(home);
|
|
30
|
+
vi.restoreAllMocks();
|
|
31
|
+
vi.unstubAllEnvs();
|
|
32
|
+
cleanScratch();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const MEMBERS = [{ name: "app", dir: "app", kind: "other", because: "the app" }];
|
|
36
|
+
const urlOf = (dir: string) => pathToFileURL(dir).href;
|
|
37
|
+
const reportsDir = () => scratchDir("chant-report-url-reports-");
|
|
38
|
+
const indexOf = (dir: string): CiRunIndex => JSON.parse(readFileSync(join(dir, CI_RUN_INDEX_KEY), "utf-8"));
|
|
39
|
+
|
|
40
|
+
/** One member and two commits. The declaration in the working tree is rewritten per test. */
|
|
41
|
+
const repoTemplate = repoFixture(() => {
|
|
42
|
+
const root = scratchDir("chant-report-url-template-");
|
|
43
|
+
git(root, "init", "-q");
|
|
44
|
+
git(root, "symbolic-ref", "HEAD", "refs/heads/main");
|
|
45
|
+
git(root, "config", "user.name", "t");
|
|
46
|
+
git(root, "config", "user.email", "t@example.com");
|
|
47
|
+
writeFiles(root, { "chant.workspace.json": declaration(MEMBERS), ".gitignore": ".chant/pr/\n", "app/a.txt": "a\n" });
|
|
48
|
+
const commit = (msg: string) => {
|
|
49
|
+
execFileSync("git", ["-c", "commit.gpgsign=false", "add", "-A"], { cwd: root, stdio: "ignore" });
|
|
50
|
+
execFileSync("git", ["-c", "commit.gpgsign=false", "commit", "-q", "-m", msg], { cwd: root, stdio: "ignore" });
|
|
51
|
+
};
|
|
52
|
+
commit("init");
|
|
53
|
+
writeFiles(root, { "app/a.txt": "b\n" });
|
|
54
|
+
commit("change");
|
|
55
|
+
return root;
|
|
56
|
+
}, "chant-report-url-");
|
|
57
|
+
|
|
58
|
+
/** A copy of the repository whose declaration sets `ci` as given, and a stub chant that writes an empty report. */
|
|
59
|
+
function workspace(ci?: Record<string, unknown>): { root: string; app: string; base: string; chant: string } {
|
|
60
|
+
const root = repoTemplate();
|
|
61
|
+
writeFiles(root, { "chant.workspace.json": declaration(MEMBERS, ci ? { ci } : {}) });
|
|
62
|
+
const chant = join(scratchDir("chant-report-url-bin-"), "chant");
|
|
63
|
+
writeFileSync(chant, ["#!/bin/sh", "mkdir -p .chant/pr", 'echo "{}" > ".chant/pr/$2.json"', "exit 0", ""].join("\n"));
|
|
64
|
+
chmodSync(chant, 0o755);
|
|
65
|
+
return { root, app: join(root, "app"), base: git(root, "rev-parse", "HEAD~1"), chant };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Run a ci command line in `cwd`, with what it printed. */
|
|
69
|
+
async function cli(cwd: string, argv: string[], run: (ctx: CommandContext) => Promise<number>) {
|
|
70
|
+
process.chdir(cwd);
|
|
71
|
+
const out: string[] = [];
|
|
72
|
+
const err: string[] = [];
|
|
73
|
+
vi.spyOn(console, "log").mockImplementation((s: string) => void out.push(String(s)));
|
|
74
|
+
vi.spyOn(console, "error").mockImplementation((s: string) => void err.push(String(s)));
|
|
75
|
+
vi.spyOn(process.stderr, "write").mockImplementation(() => true);
|
|
76
|
+
const code = await run({ args: parseArgs(argv) } as unknown as CommandContext);
|
|
77
|
+
return { code, out: out.join("\n"), err: err.join("\n") };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const runPlan = (w: ReturnType<typeof workspace>, extra: string[] = []) =>
|
|
81
|
+
cli(
|
|
82
|
+
w.app,
|
|
83
|
+
["ci", "run", "plan", "--env", "prod", "--base", w.base, "--pr", "7", "--forge", "forgejo", "--no-status", "--chant", w.chant, "-o", join(scratchDir("chant-report-url-out-"), "pr"), ...extra],
|
|
84
|
+
runCiRun,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
describe("reportUrlOf", () => {
|
|
88
|
+
test("the flag wins, else the declared URL, else none", async () => {
|
|
89
|
+
const w = workspace({ reportUrl: "s3://acme-reports/prod/" });
|
|
90
|
+
expect(await declaredReportUrl(w.app)).toBe("s3://acme-reports/prod");
|
|
91
|
+
expect(await reportUrlOf(undefined, w.root)).toBe("s3://acme-reports/prod");
|
|
92
|
+
expect(await reportUrlOf("file:///elsewhere", w.app)).toBe("file:///elsewhere");
|
|
93
|
+
expect(await reportUrlOf(undefined, workspace().app)).toBeUndefined();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("a directory outside any workspace reads nothing", async () => {
|
|
97
|
+
expect(await reportUrlOf(undefined, scratchDir("chant-report-url-plain-"))).toBeUndefined();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("a declared URL no job can upload to fails with its place and reason code, unless the flag is given", async () => {
|
|
101
|
+
const w = workspace({ reportUrl: "https://reports.example/acme" });
|
|
102
|
+
await expect(declaredReportUrl(w.app)).rejects.toThrow(/chant\.workspace\.json:\d+:\d+: ci\.reportUrl https:\/\/reports\.example\/acme: takes an s3:\/\/<bucket>\/<prefix> or file:\/\/<directory> URL \[declaration-invalid\]/);
|
|
103
|
+
expect(await reportUrlOf("file:///elsewhere", w.app)).toBe("file:///elsewhere");
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("chant workspace ls --json carries ci.reportUrl", () => {
|
|
108
|
+
const { expectValid } = contract(lsSchema);
|
|
109
|
+
const ciOf = (doc: LsDocument) => {
|
|
110
|
+
if ("error" in doc) throw new Error(doc.error.message);
|
|
111
|
+
return doc.ci;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
test("present when declared, and the document validates", () => {
|
|
115
|
+
const doc = listWorkspace({ cwd: workspace({ reportUrl: "s3://acme-reports/prod" }).root });
|
|
116
|
+
expectValid(doc);
|
|
117
|
+
expect(ciOf(doc)).toEqual({ green: null, reportUrl: "s3://acme-reports/prod" });
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("absent when the ci block sets none, and ci stays null with no ci block", () => {
|
|
121
|
+
const green = { branch: "main", phases: { build: ["build"] }, require: ["build"] };
|
|
122
|
+
const withGreen = listWorkspace({ cwd: workspace({ green }).root });
|
|
123
|
+
expectValid(withGreen);
|
|
124
|
+
expect(ciOf(withGreen)).not.toHaveProperty("reportUrl");
|
|
125
|
+
expect(ciOf(listWorkspace({ cwd: workspace().root }))).toBeNull();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("an invalid one is the declaration-invalid failure", () => {
|
|
129
|
+
const doc = listWorkspace({ cwd: workspace({ reportUrl: "ftp://x/y" }).root });
|
|
130
|
+
expectValid(doc);
|
|
131
|
+
expect(doc).toMatchObject({ error: { code: "declaration-invalid", location: { file: "chant.workspace.json" } } });
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("chant ci run", () => {
|
|
136
|
+
// The two whole jobs run once, in beforeAll, and the tests below read what
|
|
137
|
+
// they left, as ci-run.test.ts does (#4115, #4124). The first job of a file
|
|
138
|
+
// pays for loading the job's modules, which on a loaded CI shard came to
|
|
139
|
+
// over the unit budget (#2817); a file's beforeAll is not counted in it.
|
|
140
|
+
const ran: Record<"declared" | "flag", { code: number; err: string; declared: CiRunIndex | null; flag: CiRunIndex | null }> = {} as never;
|
|
141
|
+
beforeAll(async () => {
|
|
142
|
+
const read = (dir: string) => (existsSync(join(dir, CI_RUN_INDEX_KEY)) ? indexOf(dir) : null);
|
|
143
|
+
for (const kind of ["declared", "flag"] as const) {
|
|
144
|
+
const declared = reportsDir();
|
|
145
|
+
const flag = reportsDir();
|
|
146
|
+
const w = workspace({ reportUrl: urlOf(declared) });
|
|
147
|
+
const r = await runPlan(w, kind === "flag" ? ["--report-url", urlOf(flag)] : []);
|
|
148
|
+
ran[kind] = { code: r.code, err: r.err, declared: read(declared), flag: read(flag) };
|
|
149
|
+
process.chdir(home);
|
|
150
|
+
vi.restoreAllMocks();
|
|
151
|
+
}
|
|
152
|
+
}, 60_000);
|
|
153
|
+
|
|
154
|
+
test("without --report-url the job's run index goes to the declared URL", () => {
|
|
155
|
+
const r = ran.declared;
|
|
156
|
+
expect(r.code, r.err).toBe(0);
|
|
157
|
+
expect(r.declared?.runs).toHaveLength(1);
|
|
158
|
+
expect(r.declared?.runs[0]).toMatchObject({ job: "plan", pr: 7 });
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("--report-url wins over the declared URL", () => {
|
|
162
|
+
const r = ran.flag;
|
|
163
|
+
expect(r.code, r.err).toBe(0);
|
|
164
|
+
expect(r.flag?.runs).toHaveLength(1);
|
|
165
|
+
expect(r.declared).toBeNull();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("an invalid declared URL stops the job before it runs", async () => {
|
|
169
|
+
const w = workspace({ reportUrl: "https://reports.example/acme" });
|
|
170
|
+
const r = await runPlan(w);
|
|
171
|
+
expect(r.code).toBe(1);
|
|
172
|
+
expect(r.err).toMatch(/\[declaration-invalid\]/);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe("chant ci runs", () => {
|
|
177
|
+
test("lists the index at the declared URL, and --report-url wins", async () => {
|
|
178
|
+
const declared = reportsDir();
|
|
179
|
+
const w = workspace({ reportUrl: urlOf(declared) });
|
|
180
|
+
expect((await runPlan(w)).code).toBe(0);
|
|
181
|
+
|
|
182
|
+
const listed = await cli(w.app, ["ci", "runs", "--json"], runCiRuns);
|
|
183
|
+
expect(listed.code, listed.err).toBe(0);
|
|
184
|
+
expect((JSON.parse(listed.out) as CiRunIndex).runs).toHaveLength(1);
|
|
185
|
+
|
|
186
|
+
const other = reportsDir();
|
|
187
|
+
const flagged = await cli(w.app, ["ci", "runs", "--report-url", urlOf(other)], runCiRuns);
|
|
188
|
+
expect(flagged.code).toBe(1);
|
|
189
|
+
expect(flagged.err).toContain(`there is no run index at ${urlOf(other)}`);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("with neither a flag nor a declared URL it says how to name one", async () => {
|
|
193
|
+
const r = await cli(workspace().root, ["ci", "runs"], runCiRuns);
|
|
194
|
+
expect(r.code).toBe(1);
|
|
195
|
+
expect(r.err).toContain("--report-url names where the index is kept, unless the workspace declares ci.reportUrl");
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("chant ci serve", () => {
|
|
200
|
+
// No token and no server: the first poll fails, so the serve stops without reaching a forge.
|
|
201
|
+
beforeEach(() => {
|
|
202
|
+
for (const name of ["CHANT_FORGE_TOKEN", "GITHUB_TOKEN", "GH_TOKEN", "GITHUB_SERVER_URL", "CHANT_RUNNER_KEY"]) vi.stubEnv(name, undefined);
|
|
203
|
+
});
|
|
204
|
+
const SERVE = ["ci", "serve", "--forge", "forgejo", "--repo", "demo/estate", "--env", "local", "--once", "--unsigned", "--image", "node:22-bookworm"];
|
|
205
|
+
|
|
206
|
+
test("says it uses the declared URL when --report-url is not given", async () => {
|
|
207
|
+
const declared = reportsDir();
|
|
208
|
+
const w = workspace({ reportUrl: urlOf(declared) });
|
|
209
|
+
const state = join(scratchDir("chant-report-url-state-"), "ci-serve");
|
|
210
|
+
const r = await cli(w.root, [...SERVE, "--state", state], runCiServe);
|
|
211
|
+
expect(r.err).toContain(`ci serve: reports and the run index go to ${urlOf(declared)}, the declaration's ci.reportUrl`);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("says nothing of the declaration when --report-url is given", async () => {
|
|
215
|
+
const w = workspace({ reportUrl: urlOf(reportsDir()) });
|
|
216
|
+
const state = join(scratchDir("chant-report-url-state-"), "ci-serve");
|
|
217
|
+
const r = await cli(w.root, [...SERVE, "--state", state, "--report-url", urlOf(reportsDir())], runCiServe);
|
|
218
|
+
expect(r.err).not.toContain("ci.reportUrl");
|
|
219
|
+
});
|
|
220
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The report URL a command uses (#4160): `--report-url` when it is given,
|
|
3
|
+
* else the workspace declaration's `ci.reportUrl`, else none.
|
|
4
|
+
*
|
|
5
|
+
* `chant build --pr-loop`, `chant ci run`, `chant ci serve` and `chant ci
|
|
6
|
+
* runs` call this. The declaration is read from the working tree of the
|
|
7
|
+
* workspace that holds `cwd`. A directory with no declaration between it and
|
|
8
|
+
* the git root reads nothing and loads no declaration module, so a project
|
|
9
|
+
* outside a workspace behaves as before. A declaration that can't be read
|
|
10
|
+
* is an error naming its place and its reason code, such as
|
|
11
|
+
* `declaration-invalid`, since the command would otherwise run without the
|
|
12
|
+
* URL the workspace may declare.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { findWorkspaceRoot } from "../project-root";
|
|
16
|
+
|
|
17
|
+
/** The declared `ci.reportUrl` of the workspace holding `cwd`, or undefined. */
|
|
18
|
+
export async function declaredReportUrl(cwd: string): Promise<string | undefined> {
|
|
19
|
+
const found = findWorkspaceRoot(cwd);
|
|
20
|
+
if (!found) return undefined;
|
|
21
|
+
const [{ readDeclaration, WorkspaceReadError }, { workingTree }] = await Promise.all([import("./declaration"), import("./tree")]);
|
|
22
|
+
try {
|
|
23
|
+
return readDeclaration(workingTree(found.dir)).ci?.reportUrl ?? undefined;
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if (!(err instanceof WorkspaceReadError)) throw err;
|
|
26
|
+
throw Object.assign(new Error(`${err.describe()} [${err.code}]`), { code: err.code });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** `flag` when given, else the declared `ci.reportUrl` (#4160). */
|
|
31
|
+
export async function reportUrlOf(flag: string | undefined, cwd: string): Promise<string | undefined> {
|
|
32
|
+
return flag ?? (await declaredReportUrl(cwd));
|
|
33
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://intentius.io/chant/schemas/workspace/ci-run-index/v1/ci-run-index.schema.json",
|
|
4
4
|
"title": "chant ci run index",
|
|
5
|
-
"description": "The runs the chant runner has run, kept at <report-url>/runs/index.json (#4098, epic #4101) so a reader outside CI finds them without a forge or a workflow file. `chant ci run` adds an entry to the front after each job settles when --report-url is set, so the list is newest first, and keeps the newest 200 entries unless --run-index-limit says otherwise. `chant ci runs --json` prints the same document. An entry's `key` is the folder the job's report is under, below the report URL. The update is a read, a change and a write of the whole file, through the same s3:// and file:// paths the report upload uses; a file:// write goes through a temporary name and a rename. Two runners updating one index at once can lose an entry. That is out of scope, because `chant ci serve` is one host. Version 1 of the workspace read contract. Readers ignore fields they do not know; a field is only ever added within a version. Written by chant 0.124.31 and newer.",
|
|
5
|
+
"description": "The runs the chant runner has run, kept at <report-url>/runs/index.json (#4098, epic #4101) so a reader outside CI finds them without a forge or a workflow file. `chant ci run` adds an entry to the front after each job settles when --report-url is set, so the list is newest first, and keeps the newest 200 entries unless --run-index-limit says otherwise. `chant ci runs --json` prints the same document. An entry's `key` is the folder the job's report is under, below the report URL. The update is a read, a change and a write of the whole file, through the same s3:// and file:// paths the report upload uses; a file:// write goes through a temporary name and a rename. Two runners updating one index at once can lose an entry. That is out of scope, because `chant ci serve` is one host. Version 1 of the workspace read contract. Readers ignore fields they do not know; a field is only ever added within a version. Written by chant 0.124.31 and newer. Added within contract 1 at chant 0.124.32: an entry's signer (#4156) and exitCode (#4165); an entry from an older chant lacks them.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": ["$schema", "contract", "chant", "updated", "runs"],
|
|
8
8
|
"properties": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"if": { "properties": { "status": { "const": "fail" } } },
|
|
24
24
|
"then": { "required": ["failure"], "properties": { "failure": { "type": "object" } } },
|
|
25
25
|
"properties": {
|
|
26
|
-
"job": { "enum": ["plan", "apply"] },
|
|
26
|
+
"job": { "enum": ["plan", "apply", "record"], "description": "record (#4168, chant 0.124.33 and newer): the merge record job." },
|
|
27
27
|
"pr": { "type": ["integer", "null"], "minimum": 1, "description": "The pull or merge request number: the one the job's report names, else --pr. null when neither has one." },
|
|
28
28
|
"commit": { "type": "string", "pattern": "^[0-9a-f]{40,64}$", "description": "The full commit the job ran at." },
|
|
29
29
|
"status": { "enum": ["ok", "fail"] },
|
|
@@ -38,11 +38,13 @@
|
|
|
38
38
|
"ended": { "type": "string", "description": "ISO-8601 instant the job settled." },
|
|
39
39
|
"runId": { "type": "string", "description": "The Op run id, as in the run ledger on chant/lifecycle." },
|
|
40
40
|
"key": { "type": "string", "description": "reportKey(pr, commit): <pr>/<commit>, with /<member> after for a member, and unknown for a missing pr. The job's report is <report-url>/<key>/pr-<job>.json." },
|
|
41
|
+
"signer": { "type": ["string", "null"], "description": "Added within contract 1 by #4156, written by chant 0.124.32 and newer. The runner whose key signed the run's record, or null for an unsigned run. Signing is optional, and an unsigned run is listed like any other." },
|
|
42
|
+
"exitCode": { "type": "integer", "minimum": 0, "maximum": 255, "description": "Added within contract 1 by #4165, written by chant 0.124.32 and newer. The code chant ci run exited with for the job: 0 for a passed job, else the command's own code when it is 1 to 255, such as 3 for a plan that moved after review or a wave waiting at its gate, else 1. A reader tells a held run from a failed one by it when the report carries no refusal. An entry from an older chant lacks it." },
|
|
41
43
|
"runOrigin": {
|
|
42
44
|
"type": "object",
|
|
43
45
|
"required": ["forge"],
|
|
44
|
-
"description": "Where the run says it ran. { forge: \"chant\" } for a job the chant runner ran.",
|
|
45
|
-
"properties": { "forge": { "type": "string" } }
|
|
46
|
+
"description": "Where the run says it ran. { forge: \"chant\" } for a job the chant runner ran. From chant 0.124.32 a forge job that runs chant ci run names the forge's run (#4156): forge github (GitHub and Forgejo) or gitlab, with repo, url, jobId and attempt when the forge gives them.",
|
|
47
|
+
"properties": { "forge": { "type": "string" }, "repo": { "type": "string" }, "url": { "type": "string" }, "jobId": { "type": "string" }, "attempt": { "type": "integer", "minimum": 1 } }
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
}
|
|
@@ -12,7 +12,7 @@ import { join } from "node:path";
|
|
|
12
12
|
import { pathToFileURL } from "node:url";
|
|
13
13
|
import { afterAll, describe, expect, test } from "vitest";
|
|
14
14
|
import { cleanScratch, contract, declaration, git, repoFixture, scratchDir, validSchema, writeFiles } from "./__fixtures__/contract-repo";
|
|
15
|
-
import { ciRun } from "./ci-run";
|
|
15
|
+
import { ciJobExitCode, ciRun } from "./ci-run";
|
|
16
16
|
import { readCiSecrets } from "./ci-secrets";
|
|
17
17
|
import {
|
|
18
18
|
addRunEntry,
|
|
@@ -123,6 +123,13 @@ describe("the ci-run-index schema (#4098)", () => {
|
|
|
123
123
|
expect(contract(schema).validate(doc)).toBe(false);
|
|
124
124
|
expectValid({ ...doc, runs: [{ ...ENTRY, status: "fail", failure: { code: "command-failed" } }] });
|
|
125
125
|
});
|
|
126
|
+
|
|
127
|
+
test("exitCode is an integer from 0 to 255, and an index whose older entries lack it validates (#4165)", () => {
|
|
128
|
+
const doc = { $schema: CI_RUN_INDEX_SCHEMA_ID, contract: 1, chant: "0.124.32", updated: "2026-10-18T12:00:00.000Z" };
|
|
129
|
+
// A newer entry beside two from chant 0.124.31, which wrote neither signer nor exitCode.
|
|
130
|
+
expectValid({ ...doc, runs: [{ ...ENTRY, runId: "r2", signer: null, exitCode: 0 }, { ...ENTRY, runId: "r1" }, { ...ENTRY, status: "fail", failure: { code: "command-failed" } }] });
|
|
131
|
+
for (const exitCode of [-1, 256, 1.5, "3"]) expect(contract(schema).validate({ ...doc, runs: [{ ...ENTRY, exitCode }] })).toBe(false);
|
|
132
|
+
});
|
|
126
133
|
});
|
|
127
134
|
|
|
128
135
|
describe("chant ci run with --report-url keeps the run index (#4098)", () => {
|
|
@@ -149,6 +156,10 @@ describe("chant ci run with --report-url keeps the run index (#4098)", () => {
|
|
|
149
156
|
key: `7/${w.sha}/app`,
|
|
150
157
|
member: "app",
|
|
151
158
|
runOrigin: { forge: "chant" },
|
|
159
|
+
// #4156: no key, and the run is listed all the same, with no signer.
|
|
160
|
+
signer: null,
|
|
161
|
+
// #4165: the code ci run exited with.
|
|
162
|
+
exitCode: 0,
|
|
152
163
|
});
|
|
153
164
|
// The apply found its pull request in its own report.
|
|
154
165
|
expect(index.runs[0]).toMatchObject({ job: "apply", pr: 7, key: `7/${w.sha}/app` });
|
|
@@ -179,7 +190,7 @@ describe("chant ci run with --report-url keeps the run index (#4098)", () => {
|
|
|
179
190
|
const failed = await job(w, dir, { runId: "run-fail", hostEnv: { ...HOST, FAKE_EXIT: "3" }, passEnv: ["FAKE_EXIT"] });
|
|
180
191
|
expect(failed.status).toBe("fail");
|
|
181
192
|
const [run] = indexOf(dir).runs;
|
|
182
|
-
expect(run).toMatchObject({ status: "fail", failure: { code: "command-failed" }, runId: "run-fail" });
|
|
193
|
+
expect(run).toMatchObject({ status: "fail", failure: { code: "command-failed" }, runId: "run-fail", exitCode: 3 });
|
|
183
194
|
expectValid(indexOf(dir));
|
|
184
195
|
|
|
185
196
|
const refused = await job(w, dir, {
|
|
@@ -189,7 +200,35 @@ describe("chant ci run with --report-url keeps the run index (#4098)", () => {
|
|
|
189
200
|
passEnv: ["FAKE_EXIT", "FAKE_REPORT"],
|
|
190
201
|
});
|
|
191
202
|
expect(refused.status).toBe("fail");
|
|
192
|
-
expect(indexOf(dir).runs[0]).toMatchObject({ failure: { code: "not-approved" } });
|
|
203
|
+
expect(indexOf(dir).runs[0]).toMatchObject({ failure: { code: "not-approved" }, exitCode: 1 });
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("each entry records the code ci run exited with: 0, 1, and 3 for a moved plan or a waiting wave (#4165)", async () => {
|
|
207
|
+
const w = workspace();
|
|
208
|
+
const dir = reportsDir();
|
|
209
|
+
const run = (runId: string, exit: string, report: object) =>
|
|
210
|
+
job(w, dir, { job: "apply", runId, hostEnv: { ...HOST, FAKE_EXIT: exit, FAKE_REPORT: JSON.stringify({ pr: 7, ...report }) }, passEnv: ["FAKE_EXIT", "FAKE_REPORT"] });
|
|
211
|
+
const passed = await run("run-ok", "0", { status: "applied" });
|
|
212
|
+
const failed = await run("run-failed", "1", { status: "failed" });
|
|
213
|
+
const moved = await run("run-moved", "3", { status: "refused", refusal: "plan-changed", message: "the plan changed after review" });
|
|
214
|
+
const waiting = await run("run-waiting", "3", { status: "waiting", waves: [{ wave: 2, status: "waiting" }] });
|
|
215
|
+
expect([passed, failed, moved, waiting].map((r) => ciJobExitCode(r))).toEqual([0, 1, 3, 3]);
|
|
216
|
+
const index = indexOf(dir);
|
|
217
|
+
expectValid(index);
|
|
218
|
+
expect(index.runs.map((r) => [r.runId, r.status, r.exitCode])).toEqual([
|
|
219
|
+
["run-waiting", "fail", 3],
|
|
220
|
+
["run-moved", "fail", 3],
|
|
221
|
+
["run-failed", "fail", 1],
|
|
222
|
+
["run-ok", "ok", 0],
|
|
223
|
+
]);
|
|
224
|
+
expect(index.runs[1]!.failure).toEqual({ code: "plan-changed" });
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("a fail with no code from its command, or one outside 1 to 255, is 1, and a pass is 0 (#4165)", () => {
|
|
228
|
+
expect(ciJobExitCode({ status: "fail" })).toBe(1);
|
|
229
|
+
expect(ciJobExitCode({ status: "fail", exitCode: 0 })).toBe(1);
|
|
230
|
+
expect(ciJobExitCode({ status: "fail", exitCode: 300 })).toBe(1);
|
|
231
|
+
expect(ciJobExitCode({ status: "ok", exitCode: 3 })).toBe(0);
|
|
193
232
|
});
|
|
194
233
|
|
|
195
234
|
test("a secret the job's report names as its failure never reaches the index (#4096)", async () => {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
import type { RunOrigin } from "../lifecycle/release-ledger";
|
|
30
30
|
import type { CommandContext } from "../cli/registry";
|
|
31
31
|
import { formatError } from "../cli/format";
|
|
32
|
+
import { reportUrlOf } from "./ci-report-url";
|
|
32
33
|
import { parseReportUrl, reportGet, reportPut, type ReportGet, type ReportPut, type ReportTarget } from "../pr-report-url";
|
|
33
34
|
import { readerVersion } from "./declaration";
|
|
34
35
|
|
|
@@ -43,7 +44,7 @@ export const CI_RUN_INDEX_LIMIT = 200;
|
|
|
43
44
|
|
|
44
45
|
/** One job the runner ran. */
|
|
45
46
|
export interface CiRunIndexEntry {
|
|
46
|
-
job: "plan" | "apply";
|
|
47
|
+
job: "plan" | "apply" | "record";
|
|
47
48
|
/** The pull request, or null when the job found none. */
|
|
48
49
|
pr: number | null;
|
|
49
50
|
commit: string;
|
|
@@ -57,6 +58,18 @@ export interface CiRunIndexEntry {
|
|
|
57
58
|
/** The folder the job's report is under, below the report URL: `<pr>/<commit>[/<member>]`. */
|
|
58
59
|
key: string;
|
|
59
60
|
runOrigin: RunOrigin;
|
|
61
|
+
/**
|
|
62
|
+
* The runner whose key signed the run's record, or null for an unsigned
|
|
63
|
+
* run (#4156). Written by chant 0.124.32 and newer; an entry from an older
|
|
64
|
+
* chant lacks it.
|
|
65
|
+
*/
|
|
66
|
+
signer?: string | null;
|
|
67
|
+
/**
|
|
68
|
+
* The code `chant ci run` exited with for the job: 0, its command's own
|
|
69
|
+
* code such as 3 for a moved plan or a waiting wave, else 1 (#4165).
|
|
70
|
+
* Written by chant 0.124.32 and newer; an entry from an older chant lacks it.
|
|
71
|
+
*/
|
|
72
|
+
exitCode?: number;
|
|
60
73
|
}
|
|
61
74
|
|
|
62
75
|
export interface CiRunIndex {
|
|
@@ -167,18 +180,19 @@ export function formatCiRunIndex(index: CiRunIndex): string {
|
|
|
167
180
|
.map((r) => {
|
|
168
181
|
const pr = r.pr === null ? "no PR" : `PR ${r.pr}`;
|
|
169
182
|
const why = r.status === "fail" ? ` (${r.failure?.code ?? "failed"})` : "";
|
|
170
|
-
return `${r.ended} ${r.job.padEnd(5)} ${r.status}${why} ${pr} ${r.commit.slice(0, 12)} ${r.runId} ${r.key}`;
|
|
183
|
+
return `${r.ended} ${r.job.padEnd(5)} ${r.status}${why} ${pr} ${r.commit.slice(0, 12)} ${r.runId} ${r.key}${r.signer ? ` signed by ${r.signer}` : ""}`;
|
|
171
184
|
})
|
|
172
185
|
.join("\n");
|
|
173
186
|
}
|
|
174
187
|
|
|
175
|
-
const RUNS_USAGE = "chant ci runs --report-url <s3://<bucket>/<prefix> | file://<directory>> [--json]";
|
|
188
|
+
const RUNS_USAGE = "chant ci runs [--report-url <s3://<bucket>/<prefix> | file://<directory>>] [--json]";
|
|
176
189
|
|
|
177
190
|
/** `chant ci runs` (#4098): list the index at the report URL. */
|
|
178
191
|
export async function runCiRuns(ctx: CommandContext): Promise<number> {
|
|
179
192
|
try {
|
|
180
|
-
|
|
181
|
-
|
|
193
|
+
// #4160: without --report-url, the declaration's ci.reportUrl.
|
|
194
|
+
const raw = await reportUrlOf(ctx.args.reportUrl, process.cwd());
|
|
195
|
+
if (!raw) throw new Error("--report-url names where the index is kept, unless the workspace declares ci.reportUrl");
|
|
182
196
|
const target = parseReportUrl(raw);
|
|
183
197
|
if ("error" in target) throw new Error(target.error);
|
|
184
198
|
const index = await readCiRunIndex(target);
|