@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
|
@@ -41,18 +41,25 @@
|
|
|
41
41
|
* pipelines run as chant Ops, such as one on GitLab or Forgejo, gets green
|
|
42
42
|
* and revoked tags with no forge check runs. A phase's patterns then match
|
|
43
43
|
* Op names, a run counts for the commit its record names as `gitSha`, and
|
|
44
|
-
* the latest run of each Op decides, by its end time.
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
44
|
+
* the latest run of each Op decides, by its end time. Signing is opt-in
|
|
45
|
+
* (#4156): an unsigned run counts, with no signer, and a run whose statement
|
|
46
|
+
* a runner key holding the `runner` role at base signed counts with that
|
|
47
|
+
* signer (`./trust/op-run-statement.ts`). A run whose statement is there and
|
|
48
|
+
* does not verify is ignored and reported, so it neither greens nor revokes
|
|
49
|
+
* a commit. With `ci.green.signedSince`, an unsigned run counts only on a
|
|
50
|
+
* strict first-parent ancestor of that commit, as an unsigned tag does. A
|
|
51
|
+
* tick given a runner key signs the tags it makes, and each run in the
|
|
52
|
+
* tag's record names the Op run's id and carries its statement when it has
|
|
53
|
+
* one.
|
|
50
54
|
*/
|
|
51
55
|
import { judgeCommits, mergeCommits, readWorkIndex } from "./commit-links.js";
|
|
52
56
|
import { execFileSync } from "node:child_process";
|
|
53
57
|
import { readDeclaration, readerVersion, WORKSPACE_ERROR_CODES, WorkspaceReadError } from "./declaration.js";
|
|
54
58
|
import { readRunLedger } from "../lifecycle/run-ledger.js";
|
|
55
|
-
import { fetchLifecycleStatus, listFilesInDir, listLedgerTrees } from "../lifecycle/git.js";
|
|
59
|
+
import { fetchLifecycleStatus, listFilesInDir, listLedgerTrees, pushLifecycleStatus, writeLedgerFiles } from "../lifecycle/git.js";
|
|
60
|
+
import { ledgerRecordingOff } from "../lifecycle/no-record.js";
|
|
61
|
+
import { agedOut, buildRequestAnnotation, CI_BUILD_TAG_PREFIX, planBuilds, readBuildRequests, readCiRecords, recordOf, recordPath, requestedBuild, sameRecord } from "./ci-builds.js";
|
|
62
|
+
import { dispatchableWorkflows, readWorkflowSources } from "./ci-green-workflow.js";
|
|
56
63
|
import { signOpRunRecord, verifyOpRunStatement } from "./trust/op-run-statement.js";
|
|
57
64
|
import { gitTop } from "./tree.js";
|
|
58
65
|
import { locateWorkspace } from "./which-chant.js";
|
|
@@ -75,6 +82,8 @@ export class CiGreenError extends Error {
|
|
|
75
82
|
this.name = "CiGreenError";
|
|
76
83
|
}
|
|
77
84
|
}
|
|
85
|
+
/** Conclusions that say a run was stopped, or never started, rather than that it failed (#4169). */
|
|
86
|
+
const UNBUILT_CONCLUSIONS = new Set(["cancelled", "stale"]);
|
|
78
87
|
/** Whether a check run's name matches a pattern: the whole name, `*` standing for any run of characters. */
|
|
79
88
|
export function matchesCheckRun(pattern, name) {
|
|
80
89
|
const re = new RegExp(`^${pattern.split("*").map((p) => p.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*")}$`, "s");
|
|
@@ -97,6 +106,8 @@ function runVerdict(phase, r) {
|
|
|
97
106
|
return "pass";
|
|
98
107
|
if (r.conclusion === "skipped")
|
|
99
108
|
return phase.skipped === "pass" ? "pass" : "fail";
|
|
109
|
+
if (r.conclusion !== null && UNBUILT_CONCLUSIONS.has(r.conclusion))
|
|
110
|
+
return "unbuilt";
|
|
100
111
|
return "fail";
|
|
101
112
|
}
|
|
102
113
|
/** Judge one phase against a commit's latest attempts. */
|
|
@@ -113,21 +124,34 @@ export function evaluatePhase(phase, latest) {
|
|
|
113
124
|
runs.push({ name: r.name, id: r.id, status: r.status, conclusion: r.conclusion, url: r.url, verdict: runVerdict(phase, r), ...(r.opRun ? { opRun: r.opRun } : {}), ...(r.failureCode ? { failureCode: r.failureCode } : {}) });
|
|
114
125
|
}
|
|
115
126
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
return { phase: phase.name, ...phaseVerdictOf(runs, unmatched), runs, unmatched };
|
|
128
|
+
}
|
|
129
|
+
/** A phase's verdict from its runs: a failure wins, then a run still going or a pattern no run matches, then a cancelled run. */
|
|
130
|
+
export function phaseVerdictOf(runs, unmatched) {
|
|
131
|
+
if (runs.some((r) => r.verdict === "fail"))
|
|
132
|
+
return { verdict: "fail" };
|
|
133
|
+
if (runs.some((r) => r.verdict === "pending") || unmatched.length > 0)
|
|
134
|
+
return { verdict: "pending" };
|
|
135
|
+
if (runs.some((r) => r.verdict === "unbuilt"))
|
|
136
|
+
return { verdict: "unbuilt", reason: "cancelled" };
|
|
137
|
+
return { verdict: "pass" };
|
|
122
138
|
}
|
|
123
139
|
/** Judge a commit: it fails when a required phase fails, and passes when every one passes. */
|
|
124
140
|
export function evaluateCommit(green, runs) {
|
|
125
141
|
const latest = latestAttempts(runs);
|
|
126
|
-
|
|
142
|
+
return commitVerdictOf(green.require.map((name) => evaluatePhase(green.phases.find((p) => p.name === name), latest)));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* A commit's verdict from its required phases: it fails when one fails, is
|
|
146
|
+
* pending while one is, is unbuilt when one is unbuilt (#4169), and passes
|
|
147
|
+
* when every one passes.
|
|
148
|
+
*/
|
|
149
|
+
export function commitVerdictOf(phases) {
|
|
127
150
|
const failed = phases.find((p) => p.verdict === "fail");
|
|
128
151
|
if (failed)
|
|
129
152
|
return { verdict: "fail", phases, failure: { phase: failed.phase, run: failed.runs.find((r) => r.verdict === "fail") } };
|
|
130
|
-
|
|
153
|
+
const verdict = phases.some((p) => p.verdict === "pending") ? "pending" : phases.some((p) => p.verdict === "unbuilt") ? "unbuilt" : "pass";
|
|
154
|
+
return { verdict, phases, failure: null };
|
|
131
155
|
}
|
|
132
156
|
/** A time as an annotation and a tick report write it: UTC, to the second. */
|
|
133
157
|
const iso = (d) => d.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -143,7 +167,7 @@ export function windowMs(window) {
|
|
|
143
167
|
* names the Op run's id, env and signer, and carries the run's statement.
|
|
144
168
|
*/
|
|
145
169
|
const runRecord = (r) => r.opRun
|
|
146
|
-
? { name: r.name, id: null, conclusion: r.conclusion, url: r.url, opRun: { id: r.opRun.id, env: r.opRun.env, signer: r.opRun.signer, statement: r.opRun.statement } }
|
|
170
|
+
? { name: r.name, id: null, conclusion: r.conclusion, url: r.url, opRun: { id: r.opRun.id, env: r.opRun.env, signer: r.opRun.signer, ...(r.opRun.statement ? { statement: r.opRun.statement } : {}) } }
|
|
147
171
|
: { name: r.name, id: r.id, conclusion: r.conclusion, url: r.url };
|
|
148
172
|
/** `source: "runs"` on a record made from chant runs (#4044); nothing for check runs, so their records read as before. */
|
|
149
173
|
const sourceField = (green) => (green.source === "runs" ? { source: "runs" } : {});
|
|
@@ -303,6 +327,21 @@ export function readCiTags(top, green = null, base = null) {
|
|
|
303
327
|
}
|
|
304
328
|
return out;
|
|
305
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* Whether the workspace at `cwd` requires signed runs (#4156): its
|
|
332
|
+
* `ci.green.signedSince`, or null when it sets none, declares no
|
|
333
|
+
* `ci.green`, or has no declaration chant can read. Signing is opt-in, so
|
|
334
|
+
* only this makes `ci serve` refuse to start without a trusted runner key
|
|
335
|
+
* and `ci run` and `chant run` warn about a run they leave unsigned.
|
|
336
|
+
*/
|
|
337
|
+
export function signedRunsRequired(cwd) {
|
|
338
|
+
try {
|
|
339
|
+
return readDeclaration(locateWorkspace(cwd).tree).ci?.green?.signedSince ?? null;
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
306
345
|
/** The declaration's `ci.green`, read where `cwd` is, and the git top. */
|
|
307
346
|
function readGreen(cwd) {
|
|
308
347
|
const located = locateWorkspace(cwd);
|
|
@@ -376,28 +415,34 @@ export async function ciTick(req) {
|
|
|
376
415
|
if (green.signedSince && req.runnerKey === undefined && !req.dryRun) {
|
|
377
416
|
throw new Error(`ci.green.signedSince is ${green.signedSince}, so every tag the tick makes needs a statement signed by a runner key, and none was given: pass --key <runner key.pem> or set CHANT_RUNNER_KEY`);
|
|
378
417
|
}
|
|
379
|
-
if (green.source === "runs" && req.runnerKey === undefined && !req.dryRun) {
|
|
380
|
-
throw new Error("the tick reads chant runs (source runs), and every tag it makes from them is signed by a runner key, and none was given: pass --key <runner key.pem> or set CHANT_RUNNER_KEY");
|
|
381
|
-
}
|
|
382
418
|
const signer = req.runnerKey !== undefined ? ciTagSigner(top, base, req.runnerKey) : undefined;
|
|
383
419
|
const links = await commitLinks(req.cwd, top, tracking, commits.map((c) => c.sha));
|
|
384
420
|
const tags = readCiTags(top, green, base);
|
|
421
|
+
const warnings = [];
|
|
385
422
|
let runs;
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
423
|
+
// The runs and the records (#4169) live on chant/lifecycle, which a CI checkout may not have yet.
|
|
424
|
+
const fetched = await fetchLifecycleStatus({ cwd: top });
|
|
425
|
+
const ledgerOk = fetched.status !== "diverged" && fetched.status !== "unreachable";
|
|
426
|
+
if (!ledgerOk) {
|
|
427
|
+
const why = `could not fetch chant/lifecycle from ${fetched.remote}: ${fetched.status === "diverged" ? "the local branch and the remote's have diverged" : fetched.stderr}`;
|
|
428
|
+
if (green.source === "runs")
|
|
429
|
+
throw new Error(why.replace("chant/lifecycle", "chant/lifecycle, where the Op runs are recorded,"));
|
|
430
|
+
warnings.push(`${why}; no _ci records were read or written`);
|
|
393
431
|
}
|
|
432
|
+
if (green.source === "runs")
|
|
433
|
+
runs = await readOpRunSource(req.cwd, top, base, green.signedSince);
|
|
434
|
+
const runsBy = new Map();
|
|
394
435
|
const planned = await planTick({
|
|
395
436
|
green,
|
|
396
437
|
commits,
|
|
397
438
|
greenTags: new Set(tags.green.keys()),
|
|
398
439
|
revokedTags: new Set(tags.revoked.keys()),
|
|
399
440
|
ignored: tags.ignored,
|
|
400
|
-
checkRuns: async (sha) =>
|
|
441
|
+
checkRuns: async (sha) => {
|
|
442
|
+
const got = runs ? runs.checkRuns(sha) : await forge.checkRuns(sha);
|
|
443
|
+
runsBy.set(sha, got);
|
|
444
|
+
return got;
|
|
445
|
+
},
|
|
401
446
|
now,
|
|
402
447
|
signer,
|
|
403
448
|
});
|
|
@@ -411,6 +456,10 @@ export async function ciTick(req) {
|
|
|
411
456
|
c.ignoredRuns = ignored;
|
|
412
457
|
}
|
|
413
458
|
}
|
|
459
|
+
const records = ledgerOk ? readCiRecords(top) : new Map();
|
|
460
|
+
const requests = readBuildRequests(top);
|
|
461
|
+
const retryVia = green.source === "runs" ? "request" : forge?.rerun ? "forge" : null;
|
|
462
|
+
const builds = planBuilds({ green, commits: planned, runs: runsBy, requests, records, now, retryVia });
|
|
414
463
|
const due = planned.filter((c) => c.made !== null);
|
|
415
464
|
if (!req.dryRun && due.length > 0) {
|
|
416
465
|
const ident = taggerConfig(top);
|
|
@@ -426,7 +475,217 @@ export async function ciTick(req) {
|
|
|
426
475
|
throw new Error(`could not push ${due.map((c) => c.made.tag).join(", ")} to ${remote}: ${cause ? `${cause}\n` : ""}${stderr}`);
|
|
427
476
|
}
|
|
428
477
|
}
|
|
429
|
-
|
|
478
|
+
const retried = builds.retries.map((r) => ({ sha: r.sha, run: r.run.name }));
|
|
479
|
+
let recorded = [];
|
|
480
|
+
if (!req.dryRun) {
|
|
481
|
+
const asked = await askForBuilds({ top, remote, green, forge, builds, requests, records, planned, warnings });
|
|
482
|
+
recorded = ledgerOk && !ledgerRecordingOff() ? await writeRecords({ top, green, planned, records, builds, requests: asked.live, dispatched: asked.dispatched, now, warnings }) : [];
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
branch: green.branch,
|
|
486
|
+
window: green.window,
|
|
487
|
+
ref: tracking,
|
|
488
|
+
commits: planned,
|
|
489
|
+
made: due.map((c) => c.made.tag),
|
|
490
|
+
requested: builds.requests,
|
|
491
|
+
retried,
|
|
492
|
+
recorded,
|
|
493
|
+
warnings,
|
|
494
|
+
signer: signer?.principal ?? null,
|
|
495
|
+
source: green.source,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Make what the plan asks for (#4169): push each new request tag and start
|
|
500
|
+
* the workflows holding its phases at it on GitHub, start any older request
|
|
501
|
+
* no tick has started yet, run the failed jobs again, and delete the tags of
|
|
502
|
+
* requests that are done. Each step that fails is a warning, never the
|
|
503
|
+
* tick's failure: the green tags are already pushed.
|
|
504
|
+
*/
|
|
505
|
+
async function askForBuilds(input) {
|
|
506
|
+
const { top, remote, green, forge, builds, warnings } = input;
|
|
507
|
+
const live = new Map(input.requests);
|
|
508
|
+
for (const tag of builds.resolved)
|
|
509
|
+
live.delete(tag.slice(CI_BUILD_TAG_PREFIX.length));
|
|
510
|
+
if (builds.requests.length > 0) {
|
|
511
|
+
const ident = taggerConfig(top);
|
|
512
|
+
const made = [];
|
|
513
|
+
for (const r of builds.requests) {
|
|
514
|
+
if (tryGit(top, [...ident, "tag", "-a", "-m", buildRequestAnnotation(r), r.tag, r.commit]) === null)
|
|
515
|
+
warnings.push(`could not make ${r.tag}`);
|
|
516
|
+
else
|
|
517
|
+
made.push(r);
|
|
518
|
+
}
|
|
519
|
+
if (made.length > 0) {
|
|
520
|
+
try {
|
|
521
|
+
git(top, ["push", "--quiet", remote, ...made.map((r) => `refs/tags/${r.tag}`)]);
|
|
522
|
+
for (const r of made)
|
|
523
|
+
live.set(r.commit, r);
|
|
524
|
+
}
|
|
525
|
+
catch (err) {
|
|
526
|
+
for (const r of made)
|
|
527
|
+
tryGit(top, ["tag", "-d", r.tag]);
|
|
528
|
+
const stderr = stderrOf(err);
|
|
529
|
+
warnings.push(`could not push ${made.map((r) => r.tag).join(", ")} to ${remote}: ${pushRefusalCause(stderr) ?? stderr}`);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const dispatched = new Set();
|
|
534
|
+
if (green.source === "checks" && forge?.dispatch) {
|
|
535
|
+
const sources = readWorkflowSources(top, "");
|
|
536
|
+
for (const r of live.values()) {
|
|
537
|
+
if (input.records.get(r.commit)?.request?.dispatched && input.records.get(r.commit)?.request?.tag === r.tag)
|
|
538
|
+
continue;
|
|
539
|
+
// A run already going at the commit was started for it, by the tick before or by `ci run --at`.
|
|
540
|
+
const c = input.planned.find((x) => x.sha === r.commit);
|
|
541
|
+
if (c?.verdict?.phases.some((p) => r.phases.includes(p.phase) && p.runs.some((x) => x.status !== "completed"))) {
|
|
542
|
+
dispatched.add(r.commit);
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const { dispatchable, without } = dispatchableWorkflows(green, r.phases, sources);
|
|
546
|
+
for (const w of without)
|
|
547
|
+
warnings.push(`${w} holds a run of ${r.phases.join(", ")} and has no workflow_dispatch trigger, so ${r.tag} can't start it; regenerate it with chant ci workflow`);
|
|
548
|
+
let ok = dispatchable.length > 0;
|
|
549
|
+
for (const w of dispatchable) {
|
|
550
|
+
try {
|
|
551
|
+
await forge.dispatch(w, r.tag);
|
|
552
|
+
}
|
|
553
|
+
catch (err) {
|
|
554
|
+
ok = false;
|
|
555
|
+
warnings.push(`could not start ${w} at ${r.tag}: ${err instanceof Error ? err.message : String(err)}`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (ok)
|
|
559
|
+
dispatched.add(r.commit);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (forge?.rerun) {
|
|
563
|
+
for (const r of builds.retries) {
|
|
564
|
+
try {
|
|
565
|
+
await forge.rerun(r.run);
|
|
566
|
+
}
|
|
567
|
+
catch (err) {
|
|
568
|
+
warnings.push(`could not run ${r.run.name} at ${r.sha.slice(0, 12)} again, so its failure counts once the retry's hour is up: ${err instanceof Error ? err.message : String(err)}`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (builds.resolved.length > 0) {
|
|
573
|
+
for (const tag of builds.resolved)
|
|
574
|
+
tryGit(top, ["tag", "-d", tag]);
|
|
575
|
+
if (tryGit(top, ["push", "--quiet", remote, ...builds.resolved.map((t) => `:refs/tags/${t}`)]) === null) {
|
|
576
|
+
warnings.push(`could not delete ${builds.resolved.join(", ")} on ${remote}; a done request asks for nothing, so they can stay`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return { live, dispatched };
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Write each judged commit's record, and rewrite a record whose commit left
|
|
583
|
+
* the window still pending as unbuilt (#4169), in one commit on
|
|
584
|
+
* `chant/lifecycle`, and push it. Only records whose content changed are
|
|
585
|
+
* written. A push another writer beat is tried once more on the new tip.
|
|
586
|
+
*/
|
|
587
|
+
async function writeRecords(input) {
|
|
588
|
+
const { top, green, planned, records, builds, requests, now, warnings } = input;
|
|
589
|
+
const writes = [];
|
|
590
|
+
const inWindow = new Set(planned.map((c) => c.sha));
|
|
591
|
+
for (const c of planned) {
|
|
592
|
+
const previous = records.get(c.sha);
|
|
593
|
+
let record = recordOf({ green, commit: c, previous, retries: builds.retries, requests, now });
|
|
594
|
+
if (record.request) {
|
|
595
|
+
const started = input.dispatched.has(c.sha) || (previous?.request?.tag === record.request.tag && previous.request.dispatched === true);
|
|
596
|
+
if (started)
|
|
597
|
+
record = { ...record, request: { ...record.request, dispatched: true } };
|
|
598
|
+
}
|
|
599
|
+
if (!sameRecord(previous, record))
|
|
600
|
+
writes.push({ path: recordPath(c.sha), content: `${JSON.stringify(record, null, 2)}\n` });
|
|
601
|
+
}
|
|
602
|
+
for (const [sha, r] of records) {
|
|
603
|
+
if (inWindow.has(sha) || r.state !== "pending")
|
|
604
|
+
continue;
|
|
605
|
+
writes.push({ path: recordPath(sha), content: `${JSON.stringify(agedOut(r, now), null, 2)}\n` });
|
|
606
|
+
}
|
|
607
|
+
if (writes.length === 0)
|
|
608
|
+
return [];
|
|
609
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
610
|
+
try {
|
|
611
|
+
await writeLedgerFiles(writes, `ci: ${writes.length} commit record${writes.length === 1 ? "" : "s"}`, { cwd: top });
|
|
612
|
+
}
|
|
613
|
+
catch (err) {
|
|
614
|
+
warnings.push(`could not write the _ci records: ${err instanceof Error ? err.message : String(err)}`);
|
|
615
|
+
return [];
|
|
616
|
+
}
|
|
617
|
+
const pushed = await pushLifecycleStatus({ cwd: top });
|
|
618
|
+
if (pushed.status === "pushed" || pushed.status === "no-remote" || pushed.status === "not-recorded")
|
|
619
|
+
return writes.map((w) => w.path);
|
|
620
|
+
if (pushed.status !== "stale" || attempt === 1) {
|
|
621
|
+
warnings.push(`could not push the _ci records: ${pushed.stderr}`);
|
|
622
|
+
return [];
|
|
623
|
+
}
|
|
624
|
+
// Another writer pushed first: drop this commit, follow the remote, and write again on its tip.
|
|
625
|
+
tryGit(top, ["update-ref", "refs/heads/chant/lifecycle", "refs/heads/chant/lifecycle^"]);
|
|
626
|
+
const again = await fetchLifecycleStatus({ cwd: top });
|
|
627
|
+
if (again.status === "diverged" || again.status === "unreachable") {
|
|
628
|
+
warnings.push(`could not push the _ci records: chant/lifecycle moved on the remote and could not be fetched again`);
|
|
629
|
+
return [];
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return [];
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* `chant ci run --at <sha>`: ask for every required phase to be built at a
|
|
636
|
+
* commit of the branch, by pushing `ci/build/<sha>`. Given a forge that can
|
|
637
|
+
* start workflows, and with the source check runs, it starts the workflows
|
|
638
|
+
* holding the phases at the tag now; otherwise the next tick does, or `ci
|
|
639
|
+
* serve --build-command` for chant runs. Once the phases pass, the tick tags
|
|
640
|
+
* the commit green like any other, so it can be cut.
|
|
641
|
+
*/
|
|
642
|
+
export async function requestBuild(req) {
|
|
643
|
+
const read = readGreen(req.cwd);
|
|
644
|
+
const { green, top } = read;
|
|
645
|
+
const remote = req.remote ?? "origin";
|
|
646
|
+
const tracking = `refs/remotes/${remote}/${green.branch}`;
|
|
647
|
+
try {
|
|
648
|
+
git(top, ["fetch", "--quiet", "--no-tags", remote, `+refs/heads/${green.branch}:${tracking}`, `+refs/tags/${CI_BUILD_TAG_PREFIX}*:refs/tags/${CI_BUILD_TAG_PREFIX}*`]);
|
|
649
|
+
}
|
|
650
|
+
catch (err) {
|
|
651
|
+
throw new Error(`could not fetch ${green.branch} and the build requests from ${remote}: ${stderrOf(err)}`);
|
|
652
|
+
}
|
|
653
|
+
if (req.at.startsWith("-") || tryGit(top, ["rev-parse", "--verify", "--quiet", `${req.at}^{commit}`]) === null)
|
|
654
|
+
throw new WorkspaceReadError("revision-unknown", `--at ${req.at} names no commit in this repository`);
|
|
655
|
+
const sha = git(top, ["rev-parse", `${req.at}^{commit}`]);
|
|
656
|
+
const onBranch = git(top, ["rev-list", "--first-parent", tracking]).split("\n").includes(sha);
|
|
657
|
+
if (!onBranch)
|
|
658
|
+
throw new Error(`${sha.slice(0, 12)} is not a first-parent commit of ${remote}/${green.branch}, and only those are judged green`);
|
|
659
|
+
const existing = readBuildRequests(top).get(sha);
|
|
660
|
+
const request = existing ?? requestedBuild(green, sha, green.require, req.now ?? new Date());
|
|
661
|
+
const warnings = [];
|
|
662
|
+
if (!existing) {
|
|
663
|
+
git(top, [...taggerConfig(top), "tag", "-a", "-m", buildRequestAnnotation(request), request.tag, sha]);
|
|
664
|
+
try {
|
|
665
|
+
git(top, ["push", "--quiet", remote, `refs/tags/${request.tag}`]);
|
|
666
|
+
}
|
|
667
|
+
catch (err) {
|
|
668
|
+
tryGit(top, ["tag", "-d", request.tag]);
|
|
669
|
+
const stderr = stderrOf(err);
|
|
670
|
+
throw new Error(`could not push ${request.tag} to ${remote}: ${pushRefusalCause(stderr) ?? stderr}`);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
const dispatched = [];
|
|
674
|
+
if (green.source === "checks" && req.forge?.dispatch) {
|
|
675
|
+
const { dispatchable, without } = dispatchableWorkflows(green, request.phases, readWorkflowSources(top, ""));
|
|
676
|
+
for (const w of without)
|
|
677
|
+
warnings.push(`${w} holds a run of a required phase and has no workflow_dispatch trigger, so it can't be started at ${request.tag}`);
|
|
678
|
+
for (const w of dispatchable) {
|
|
679
|
+
try {
|
|
680
|
+
await req.forge.dispatch(w, request.tag);
|
|
681
|
+
dispatched.push(w);
|
|
682
|
+
}
|
|
683
|
+
catch (err) {
|
|
684
|
+
warnings.push(`could not start ${w} at ${request.tag}: ${err instanceof Error ? err.message : String(err)}; the next tick tries again`);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return { request, existed: existing !== undefined, dispatched, warnings };
|
|
430
689
|
}
|
|
431
690
|
const RUN_LEDGER_FILE = /^runs__(.+)\.jsonl$/;
|
|
432
691
|
/**
|
|
@@ -445,18 +704,23 @@ export function opRunCheckRun(record, signer) {
|
|
|
445
704
|
conclusion: settled ? (record.status === "ok" ? "success" : "failure") : null,
|
|
446
705
|
completedAt: settled ? record.ended : null,
|
|
447
706
|
url: record.runOrigin?.url ?? null,
|
|
448
|
-
opRun: { id: record.id, env: record.env, signer, statement: record.statement },
|
|
707
|
+
opRun: { id: record.id, env: record.env, signer, ...(signer !== null && record.statement ? { statement: record.statement } : {}) },
|
|
449
708
|
...(record.status === "fail" && record.failure ? { failureCode: record.failure.code } : {}),
|
|
450
709
|
};
|
|
451
710
|
}
|
|
452
711
|
/**
|
|
453
712
|
* Judge every Op run record that names a commit against `.chant/trust.json`
|
|
454
713
|
* at `base` (#4044), from the run ledger the project at `cwd` writes to. A
|
|
455
|
-
* record with a valid statement becomes a check run on its `gitSha
|
|
456
|
-
*
|
|
714
|
+
* record with a valid statement becomes a check run on its `gitSha` with
|
|
715
|
+
* its signer, and an unsigned one becomes a check run with signer null
|
|
716
|
+
* (#4156). A record whose statement does not verify is kept as an ignored
|
|
717
|
+
* run. With `signedSince` (`ci.green.signedSince`) an unsigned record counts
|
|
718
|
+
* only on a strict first-parent ancestor of that commit, and is ignored
|
|
719
|
+
* elsewhere. Reads local refs only.
|
|
457
720
|
*/
|
|
458
|
-
export async function readOpRunSource(cwd, top, base) {
|
|
721
|
+
export async function readOpRunSource(cwd, top, base, signedSince = null) {
|
|
459
722
|
const policy = readTrustPolicy(gitRevisionSource(top, base), base);
|
|
723
|
+
const before = signedSince ? unsignedHistory(top, signedSince) : null;
|
|
460
724
|
const byCommit = new Map();
|
|
461
725
|
const ignored = new Map();
|
|
462
726
|
for (const env of await listLedgerTrees({ cwd })) {
|
|
@@ -468,11 +732,17 @@ export async function readOpRunSource(cwd, top, base) {
|
|
|
468
732
|
for (const record of records) {
|
|
469
733
|
if (!record.gitSha)
|
|
470
734
|
continue;
|
|
735
|
+
// #4161: a job `ci run --local` ran stays on the machine that ran it, and is never CI's verdict.
|
|
736
|
+
if (record.runOrigin?.forge === "local")
|
|
737
|
+
continue;
|
|
471
738
|
const v = verifyOpRunStatement(record, policy.runners, policy.roles);
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
739
|
+
const counts = v.status === "signed" || (v.status === "unsigned" && (before === null || before.has(record.gitSha)));
|
|
740
|
+
if (counts)
|
|
741
|
+
byCommit.set(record.gitSha, [...(byCommit.get(record.gitSha) ?? []), opRunCheckRun(record, v.status === "signed" ? v.signer : null)]);
|
|
742
|
+
else {
|
|
743
|
+
const reason = v.status === "unsigned" ? `${v.reason}; ci.green.signedSince is ${signedSince}, so a run on a commit that is not a strict first-parent ancestor of it needs one` : v.reason;
|
|
744
|
+
ignored.set(record.gitSha, [...(ignored.get(record.gitSha) ?? []), { run: record.id, op: record.op, env: record.env, status: v.status, reason }]);
|
|
745
|
+
}
|
|
476
746
|
}
|
|
477
747
|
}
|
|
478
748
|
}
|
|
@@ -562,8 +832,17 @@ export function formatTick(r, dryRun = false) {
|
|
|
562
832
|
}
|
|
563
833
|
else {
|
|
564
834
|
const v = c.verdict;
|
|
565
|
-
const
|
|
566
|
-
const
|
|
835
|
+
const why = (p) => `${p.phase}${p.reason ? ` (${p.reason}${p.coveredBy ? `, covered by ${p.coveredBy.slice(0, 12)}` : ""})` : ""}`;
|
|
836
|
+
const waiting = v.phases.filter((p) => p.verdict === "pending").map(why);
|
|
837
|
+
const unbuilt = v.phases.filter((p) => p.verdict === "unbuilt").map(why);
|
|
838
|
+
const culprit = v.phases.find((p) => p.culprit)?.culprit;
|
|
839
|
+
const what = c.before === "green"
|
|
840
|
+
? "green"
|
|
841
|
+
: v.verdict === "fail"
|
|
842
|
+
? `failed: phase ${v.failure.phase}, ${v.failure.run.name} ${v.failure.run.conclusion}${culprit ? `; culprit ${culprit.slice(0, 12)}` : ""}`
|
|
843
|
+
: v.verdict === "unbuilt"
|
|
844
|
+
? `unbuilt: ${unbuilt.join(", ")}`
|
|
845
|
+
: `undecided: ${waiting.join(", ")}`;
|
|
567
846
|
lines.push(` ${short} ${what}`);
|
|
568
847
|
}
|
|
569
848
|
const why = whyWorkUnknown(c.work);
|
|
@@ -578,6 +857,14 @@ export function formatTick(r, dryRun = false) {
|
|
|
578
857
|
}
|
|
579
858
|
if (r.signer && r.made.length > 0)
|
|
580
859
|
lines.push(`signed as ${r.signer}`);
|
|
860
|
+
for (const q of r.requested)
|
|
861
|
+
lines.push(`${dryRun ? "would ask" : "asked"} for ${q.phases.join(", ")} at ${q.commit.slice(0, 12)} (${q.reason}): ${q.tag}`);
|
|
862
|
+
for (const x of r.retried)
|
|
863
|
+
lines.push(`${dryRun ? "would run" : "ran"} ${x.run} at ${x.sha.slice(0, 12)} again`);
|
|
864
|
+
if (r.recorded.length > 0)
|
|
865
|
+
lines.push(`recorded ${r.recorded.length} commit${r.recorded.length === 1 ? "" : "s"} on chant/lifecycle`);
|
|
866
|
+
for (const w of r.warnings)
|
|
867
|
+
lines.push(`warning: ${w}`);
|
|
581
868
|
return lines.join("\n");
|
|
582
869
|
}
|
|
583
870
|
// ── last-green ───────────────────────────────────────────────────────────────
|
|
@@ -589,6 +876,8 @@ function branchRefOf(top, green, remote = "origin") {
|
|
|
589
876
|
throw new CiGreenError("ci-branch-unknown", `this repository has neither ${candidates.join(" nor ")}`);
|
|
590
877
|
return ref;
|
|
591
878
|
}
|
|
879
|
+
/** How many commits above the green one `ci last-green` reports with their records. */
|
|
880
|
+
export const LAST_GREEN_NEWER = 50;
|
|
592
881
|
/**
|
|
593
882
|
* `chant ci last-green`: the newest first-parent commit of the branch with a
|
|
594
883
|
* green tag and no revoked tag. It reads local refs and tags and never
|
|
@@ -610,16 +899,21 @@ export function lastGreen(req) {
|
|
|
610
899
|
const tags = readCiTags(top, green, git(top, ["rev-parse", `${branchRef}^{commit}`]));
|
|
611
900
|
let commit = null;
|
|
612
901
|
const ignored = [];
|
|
613
|
-
|
|
902
|
+
const newer = [];
|
|
903
|
+
const records = readCiRecords(top);
|
|
904
|
+
if (tags.green.size > 0 || tags.ignored.size > 0 || records.size > 0) {
|
|
614
905
|
for (const sha of git(top, ["rev-list", "--first-parent", ref]).split("\n")) {
|
|
615
906
|
ignored.push(...(tags.ignored.get(sha) ?? []));
|
|
616
|
-
if (!tags.green.has(sha) || tags.revoked.has(sha))
|
|
907
|
+
if (!tags.green.has(sha) || tags.revoked.has(sha)) {
|
|
908
|
+
if (newer.length < LAST_GREEN_NEWER)
|
|
909
|
+
newer.push({ sha, record: recordSummary(green, records.get(sha)) });
|
|
617
910
|
continue;
|
|
911
|
+
}
|
|
618
912
|
commit = { sha, tag: `${CI_GREEN_TAG_PREFIX}${sha}`, tagged: tags.green.get(sha).tagged, committed: git(top, ["show", "-s", "--format=%cI", sha]) };
|
|
619
913
|
break;
|
|
620
914
|
}
|
|
621
915
|
}
|
|
622
|
-
return { ...head, workspace, branch: green.branch, ref, commit, ignored };
|
|
916
|
+
return { ...head, workspace, branch: green.branch, ref, commit, ignored, newer: commit ? newer : [] };
|
|
623
917
|
}
|
|
624
918
|
catch (err) {
|
|
625
919
|
if (err instanceof CiGreenError || err instanceof WorkspaceReadError)
|
|
@@ -630,6 +924,17 @@ export function lastGreen(req) {
|
|
|
630
924
|
// ── list ─────────────────────────────────────────────────────────────────────
|
|
631
925
|
export const CI_LIST_CONTRACT_VERSION = 1;
|
|
632
926
|
export const CI_LIST_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/ci-list/v1/ci-list.schema.json";
|
|
927
|
+
/** The summary of a record, from its required phases in `require` order. */
|
|
928
|
+
export function recordSummary(green, r) {
|
|
929
|
+
if (!r)
|
|
930
|
+
return null;
|
|
931
|
+
const phases = green.require.map((name) => r.phases[name]).filter((p) => p !== undefined);
|
|
932
|
+
const first = (k) => phases.find((p) => p[k] !== undefined)?.[k];
|
|
933
|
+
const reason = first("reason");
|
|
934
|
+
const coveredBy = first("coveredBy");
|
|
935
|
+
const culprit = first("culprit");
|
|
936
|
+
return { state: r.state, ...(reason ? { reason } : {}), ...(coveredBy ? { coveredBy } : {}), ...(culprit ? { culprit } : {}) };
|
|
937
|
+
}
|
|
633
938
|
const asRun = (r) => {
|
|
634
939
|
if (!r || typeof r !== "object")
|
|
635
940
|
return null;
|
|
@@ -637,7 +942,8 @@ const asRun = (r) => {
|
|
|
637
942
|
if (typeof o.name !== "string")
|
|
638
943
|
return null;
|
|
639
944
|
const op = o.opRun;
|
|
640
|
-
const
|
|
945
|
+
const signer = op && typeof op === "object" ? (typeof op.signer === "string" ? op.signer : op.signer === null ? null : undefined) : undefined;
|
|
946
|
+
const opRun = op && typeof op === "object" && typeof op.id === "string" && typeof op.env === "string" && signer !== undefined ? { opRun: { id: op.id, env: op.env, signer } } : {};
|
|
641
947
|
return { name: o.name, id: typeof o.id === "number" ? o.id : null, conclusion: typeof o.conclusion === "string" ? o.conclusion : null, url: typeof o.url === "string" ? o.url : null, ...opRun };
|
|
642
948
|
};
|
|
643
949
|
/**
|
|
@@ -693,7 +999,8 @@ export async function ciList(req) {
|
|
|
693
999
|
const source = req.source ?? green.source;
|
|
694
1000
|
if (source === "runs" && req.forge)
|
|
695
1001
|
throw new Error("--forge reads check runs, and the source is chant runs (ci.green.source or --source runs); drop one");
|
|
696
|
-
const runs = source === "runs" ? await readOpRunSource(req.cwd, top, base) : undefined;
|
|
1002
|
+
const runs = source === "runs" ? await readOpRunSource(req.cwd, top, base, green.signedSince) : undefined;
|
|
1003
|
+
const records = readCiRecords(top);
|
|
697
1004
|
const out = [];
|
|
698
1005
|
for (const c of commits) {
|
|
699
1006
|
const g = tags.green.get(c.sha);
|
|
@@ -719,6 +1026,7 @@ export async function ciList(req) {
|
|
|
719
1026
|
work: links?.get(c.sha) ?? null,
|
|
720
1027
|
ignored: tags.ignored.get(c.sha) ?? [],
|
|
721
1028
|
...(runs ? { ignoredRuns: runs.ignored.get(c.sha) ?? [] } : {}),
|
|
1029
|
+
record: recordSummary(green, records.get(c.sha)),
|
|
722
1030
|
});
|
|
723
1031
|
}
|
|
724
1032
|
return { ...head, workspace, branch: green.branch, ref, window: req.since === undefined ? green.window : null, since: req.since ?? null, require: [...green.require], commits: out };
|
|
@@ -729,6 +1037,11 @@ export async function ciList(req) {
|
|
|
729
1037
|
throw err;
|
|
730
1038
|
}
|
|
731
1039
|
}
|
|
1040
|
+
/** A record summary in words, for `ci list` and the tick. */
|
|
1041
|
+
export function describeRecord(r) {
|
|
1042
|
+
const parts = [r.state, ...(r.reason ? [r.reason] : []), ...(r.coveredBy ? [`covered by ${r.coveredBy.slice(0, 12)}`] : []), ...(r.culprit ? [`culprit ${r.culprit.slice(0, 12)}`] : [])];
|
|
1043
|
+
return parts.join(", ");
|
|
1044
|
+
}
|
|
732
1045
|
/** The text `chant ci list` prints: one line per commit. */
|
|
733
1046
|
export function formatList(doc) {
|
|
734
1047
|
const lines = [`${doc.branch}, ${doc.since ? `commits after ${doc.since}` : `commits within ${doc.window}`}: ${doc.commits.length}`];
|
|
@@ -736,6 +1049,8 @@ export function formatList(doc) {
|
|
|
736
1049
|
const phases = c.phases.map((p) => `${p.phase}=${p.result}`).join(" ");
|
|
737
1050
|
const why = whyWorkUnknown(c.work);
|
|
738
1051
|
lines.push(` ${c.sha.slice(0, 12)} ${c.state.padEnd(9)} ${phases}${why ? `; why unknown: ${why}` : ""}`);
|
|
1052
|
+
if (c.record && c.state === "undecided")
|
|
1053
|
+
lines.push(` recorded ${describeRecord(c.record)}`);
|
|
739
1054
|
for (const t of c.ignored)
|
|
740
1055
|
lines.push(` ignored ${t.tag} (${t.status}): ${t.reason}`);
|
|
741
1056
|
for (const r of c.ignoredRuns ?? [])
|
|
@@ -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.",
|