@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
|
@@ -24,6 +24,8 @@ const writtenTemplates: unknown[] = [];
|
|
|
24
24
|
let configEnvironments: unknown[] | undefined;
|
|
25
25
|
let publishedDigest = "sha256:api";
|
|
26
26
|
let failApplyFor: string | undefined;
|
|
27
|
+
/** What `failApplyFor`'s apply throws; a plain error when unset. */
|
|
28
|
+
let applyError: Error | undefined;
|
|
27
29
|
|
|
28
30
|
vi.mock("../../lifecycle/git", () => ({
|
|
29
31
|
getHeadCommit: async () => "abc123",
|
|
@@ -58,7 +60,7 @@ vi.mock("../../components/fan-out-support", () => ({
|
|
|
58
60
|
step("docker-build", () => ({ digest: "sha256:rebuilt" }));
|
|
59
61
|
step("publish-image", () => ({ digest: publishedDigest, uri: `reg/api@${publishedDigest}` }));
|
|
60
62
|
step("apply", (ctx) => {
|
|
61
|
-
if (ctx.component === failApplyFor) throw new Error("apply failed");
|
|
63
|
+
if (ctx.component === failApplyFor) throw applyError ?? new Error("apply failed");
|
|
62
64
|
return {};
|
|
63
65
|
});
|
|
64
66
|
// #4045: a planner whose member is provisional.
|
|
@@ -154,6 +156,12 @@ vi.mock("../../lifecycle/release-revocation", async () => {
|
|
|
154
156
|
return { ...actual, readRevokedCommits: async () => revokedCommits };
|
|
155
157
|
});
|
|
156
158
|
|
|
159
|
+
// #4168: the security groups and the release verification, set per test.
|
|
160
|
+
let groups: string[] = [];
|
|
161
|
+
let verification: { status: "verified" | "unsigned" | "unverified"; digestMatches: boolean; reason: string } = { status: "unsigned", digestMatches: true, reason: "no cut of the release carries a signature" };
|
|
162
|
+
vi.mock("../../workspace/security", async (original) => ({ ...(await original<typeof import("../../workspace/security")>()), securityGroupsAt: () => new Set(groups) }));
|
|
163
|
+
vi.mock("../../lifecycle/release-verify", async (original) => ({ ...(await original<typeof import("../../lifecycle/release-verify")>()), verifyRelease: async () => verification }));
|
|
164
|
+
|
|
157
165
|
// The run handler pulls in every runtime; the promote needs only its exit code.
|
|
158
166
|
vi.mock("./run", () => ({ GATED_EXIT_CODE: 3 }));
|
|
159
167
|
|
|
@@ -212,6 +220,8 @@ beforeEach(() => {
|
|
|
212
220
|
knownLabels.clear();
|
|
213
221
|
outcomes.length = 0;
|
|
214
222
|
revokedCommits.clear();
|
|
223
|
+
groups = [];
|
|
224
|
+
verification = { status: "unsigned", digestMatches: true, reason: "no cut of the release carries a signature" };
|
|
215
225
|
removedTemplates.length = 0;
|
|
216
226
|
ran.length = 0;
|
|
217
227
|
stepInputs.length = 0;
|
|
@@ -221,6 +231,7 @@ beforeEach(() => {
|
|
|
221
231
|
configEnvironments = undefined;
|
|
222
232
|
publishedDigest = "sha256:api";
|
|
223
233
|
failApplyFor = undefined;
|
|
234
|
+
applyError = undefined;
|
|
224
235
|
errors = [];
|
|
225
236
|
logs = [];
|
|
226
237
|
vi.spyOn(console, "error").mockImplementation((...m: unknown[]) => { errors.push(m.join(" ")); });
|
|
@@ -358,6 +369,21 @@ describe("chant components promote", () => {
|
|
|
358
369
|
expect(appendReleaseRecordMock.mock.calls[0][0]).toMatchObject({ component: "api", env: "prod" });
|
|
359
370
|
});
|
|
360
371
|
|
|
372
|
+
test.each([
|
|
373
|
+
["marked planMoved", 3, { planMoved: true }],
|
|
374
|
+
["with exitCode 3", 3, { exitCode: 3 }],
|
|
375
|
+
["with exitCode 7", 1, { exitCode: 7 }],
|
|
376
|
+
])("an apply that refuses its plan %s exits %i, and what deployed is still recorded (#3997)", async (_label, code, mark) => {
|
|
377
|
+
const web: DriverComponent = { ...api, name: "web" };
|
|
378
|
+
resolveTargetsMock.mockResolvedValue({ success: true, targets: [api, web] });
|
|
379
|
+
readReleaseLedgerMock.mockResolvedValue({ records: [staging, { ...staging, component: "web" }], malformed: 0 });
|
|
380
|
+
failApplyFor = "web";
|
|
381
|
+
applyError = Object.assign(new Error("web: refusing to apply, the plan moved"), mark);
|
|
382
|
+
expect(await runComponentsPromote(ctx({}))).toBe(code);
|
|
383
|
+
expect(appendReleaseRecordMock).toHaveBeenCalledTimes(1);
|
|
384
|
+
expect(errors.join("\n")).toMatch(/promote failed at "web"/);
|
|
385
|
+
});
|
|
386
|
+
|
|
361
387
|
test("--digest <component>=<digest> promotes the pinned release, not the latest one (#2602)", async () => {
|
|
362
388
|
const web: DriverComponent = { ...api, name: "web" };
|
|
363
389
|
resolveTargetsMock.mockResolvedValue({ success: true, targets: [api, web] });
|
|
@@ -553,6 +579,15 @@ describe("chant components promote --release binds its gate (#4045)", () => {
|
|
|
553
579
|
expect(appendReleaseRecordMock).not.toHaveBeenCalled();
|
|
554
580
|
});
|
|
555
581
|
|
|
582
|
+
test("under verified-releases a release that does not verify is refused before anything runs, and one that does goes on to its gate (#4168)", async () => {
|
|
583
|
+
groups = ["verified-releases"];
|
|
584
|
+
expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(1);
|
|
585
|
+
expect(errors.join("\n")).toMatch(/release-unsigned: release v1\.0\.0 does not verify \(no cut of the release carries a signature\), and the workspace opts into verified-releases/);
|
|
586
|
+
expect(ran).toEqual([]);
|
|
587
|
+
verification = { status: "verified", digestMatches: true, reason: "signed by alice" };
|
|
588
|
+
expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(3);
|
|
589
|
+
});
|
|
590
|
+
|
|
556
591
|
test("an approval for one release can't be used by another", async () => {
|
|
557
592
|
expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(3);
|
|
558
593
|
const pending = releaseGatePort!.appended[0];
|
|
@@ -795,6 +830,13 @@ describe("chant components redeploy", () => {
|
|
|
795
830
|
]);
|
|
796
831
|
expect(errors.join("\n")).toMatch(/redeploy failed at "api"/);
|
|
797
832
|
});
|
|
833
|
+
|
|
834
|
+
test("a redeploy whose apply refuses a moved plan exits 3 (#3997)", async () => {
|
|
835
|
+
failApplyFor = "api";
|
|
836
|
+
applyError = Object.assign(new Error("api: refusing to apply, the plan moved"), { planMoved: true });
|
|
837
|
+
expect(await runComponentsRedeploy(rd({}))).toBe(3);
|
|
838
|
+
expect(appendReleaseRecordMock).not.toHaveBeenCalled();
|
|
839
|
+
});
|
|
798
840
|
});
|
|
799
841
|
|
|
800
842
|
describe("--events-json on promote, rollback and redeploy (#4041)", () => {
|
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
type PromotionItem,
|
|
59
59
|
type PromotionPlan,
|
|
60
60
|
} from "../../components/promote";
|
|
61
|
-
import type
|
|
61
|
+
import { failedRunExitCode, type DriverComponent } from "../../components/driver";
|
|
62
62
|
import { summaryLedgerPrefix, writeGatedRunSummary } from "../../op/gate-summary";
|
|
63
63
|
import { approveCommand, describeGateMismatch, gateIsSealed, gitGateLedgerPort, type GateLedgerPort } from "../../op/gate";
|
|
64
64
|
import type { ReleaseGateBinding } from "../../components/driver";
|
|
@@ -315,7 +315,7 @@ async function deployPlan(
|
|
|
315
315
|
if (under) {
|
|
316
316
|
const { pinReleaseTemplates } = await import("../../components/release-promote");
|
|
317
317
|
const { readReleaseTemplate } = await import("../../lifecycle/release-store");
|
|
318
|
-
const pinned = await pinReleaseTemplates(prepared, under.release, under.target, (digest) => readReleaseTemplate(digest));
|
|
318
|
+
const pinned = await pinReleaseTemplates(prepared, under.release, under.target, (digest, encoding) => readReleaseTemplate(digest, undefined, encoding));
|
|
319
319
|
if ("errors" in pinned) {
|
|
320
320
|
for (const message of pinned.errors) console.error(formatError({ message }));
|
|
321
321
|
console.error(formatError({ message: `nothing was deployed` }));
|
|
@@ -522,7 +522,8 @@ async function deployPlan(
|
|
|
522
522
|
|
|
523
523
|
if (run.status !== "ok") {
|
|
524
524
|
console.error(formatError({ message: `${verb} failed at "${run.failedComponent ?? "unknown"}"` }));
|
|
525
|
-
|
|
525
|
+
// A step that refused a plan that moved since it was approved exits 3 (#3997).
|
|
526
|
+
return failedRunExitCode(run.results);
|
|
526
527
|
}
|
|
527
528
|
return 0;
|
|
528
529
|
}
|
|
@@ -594,6 +595,21 @@ async function resolveUnderRelease(ref: string, env: string, verb: "promote" | "
|
|
|
594
595
|
console.error(formatError(revokedRefusal({ releaseDigest: found.release.releaseDigest, label: found.label }, revocation, await fix(found.release))));
|
|
595
596
|
return 1;
|
|
596
597
|
}
|
|
598
|
+
// #4168: under verified-releases a release deploys only once `chant release verify` passes for it.
|
|
599
|
+
const { securityGroupsAt } = await import("../../workspace/security");
|
|
600
|
+
if (securityGroupsAt(process.cwd()).has("verified-releases")) {
|
|
601
|
+
const { verifyRelease, verificationCode } = await import("../../lifecycle/release-verify");
|
|
602
|
+
const name = found.label ?? found.release.releaseDigest;
|
|
603
|
+
const v = await verifyRelease({ cwd: process.cwd(), ref: found.release.releaseDigest });
|
|
604
|
+
const code = "error" in v ? v.error.code : verificationCode(v);
|
|
605
|
+
if (code) {
|
|
606
|
+
console.error(formatError({
|
|
607
|
+
message: `${code}: release ${name} does not verify (${"error" in v ? v.error.message : v.reason}), and the workspace opts into verified-releases, so ${verb} deploys only a verified release`,
|
|
608
|
+
hint: `Check it with: chant release verify ${name}`,
|
|
609
|
+
}));
|
|
610
|
+
return 1;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
597
613
|
return { release: found.release, label: found.label, target: target.target };
|
|
598
614
|
}
|
|
599
615
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant release cut --at <sha>` and `chant release candidates` end to end
|
|
3
|
+
* (#4168 4.2): a cut of a chosen green commit of trunk from a temporary
|
|
4
|
+
* worktree, while the checkout sits elsewhere with uncommitted changes, with
|
|
5
|
+
* the label reaching the build as CHANT_RELEASE_LABEL (decision 5). A temp
|
|
6
|
+
* repo with a temp bare remote; no cloud, no push of tags.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, test, expect } from "vitest";
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { pathToFileURL } from "node:url";
|
|
15
|
+
import { contract, REPO } from "../../workspace/__fixtures__/contract-repo";
|
|
16
|
+
import candidatesSchema from "../../workspace/release-candidates.schema.json";
|
|
17
|
+
|
|
18
|
+
const CHANT = [process.execPath, "--import", pathToFileURL(join(REPO, "node_modules", "tsx", "dist", "loader.mjs")).href, join(REPO, "packages", "core", "src", "cli", "main.ts")];
|
|
19
|
+
const CI_VARS = ["CI", "GITHUB_ACTIONS", "GITHUB_RUN_ID", "GITHUB_SHA", "GITHUB_ACTOR", "GITLAB_CI", "CI_PIPELINE_ID", "CHANT_ENV", "CHANT_RELEASE_LABEL"];
|
|
20
|
+
const { expectValid } = contract(candidatesSchema);
|
|
21
|
+
|
|
22
|
+
function chant(cwd: string, args: string[], extra: Record<string, string> = {}): { code: number; stdout: string; stderr: string } {
|
|
23
|
+
const env: NodeJS.ProcessEnv = { ...process.env, USER: "tester" };
|
|
24
|
+
for (const k of CI_VARS) delete env[k];
|
|
25
|
+
Object.assign(env, extra);
|
|
26
|
+
const r = spawnSync(CHANT[0], [...CHANT.slice(1), ...args], { cwd, encoding: "utf-8", env, timeout: 120_000 });
|
|
27
|
+
return { code: r.status ?? 1, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function git(cwd: string, ...args: string[]): string {
|
|
31
|
+
const r = spawnSync("git", args, { cwd, encoding: "utf-8" });
|
|
32
|
+
if (r.status !== 0) throw new Error(r.stderr);
|
|
33
|
+
return r.stdout.trim();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// The template records the environment, the label, and which commit's file the build read.
|
|
37
|
+
const BUILD = `mkdir -p dist && printf '{"env":"%s","label":"%s","marker":"%s"}' "$CHANT_ENV" "$CHANT_RELEASE_LABEL" "$(cat marker.txt)" > dist/api.template.json`;
|
|
38
|
+
|
|
39
|
+
describe("chant release cut --at and release candidates", () => {
|
|
40
|
+
test(
|
|
41
|
+
"cuts a chosen green commit from a temporary worktree with the label in the build, and lists candidates",
|
|
42
|
+
() => {
|
|
43
|
+
const root = mkdtempSync(join(tmpdir(), "chant-release-cut-at-"));
|
|
44
|
+
try {
|
|
45
|
+
const dir = join(root, "work");
|
|
46
|
+
const remote = join(root, "origin.git");
|
|
47
|
+
git(root, "init", "-q", "--bare", "-b", "main", remote);
|
|
48
|
+
git(root, "init", "-q", "-b", "main", dir);
|
|
49
|
+
git(dir, "config", "user.email", "t@chant.dev");
|
|
50
|
+
git(dir, "config", "user.name", "T");
|
|
51
|
+
writeFileSync(join(dir, "chant.config.ts"), `export default { environments: ["dev", "prod"] };\n`);
|
|
52
|
+
writeFileSync(
|
|
53
|
+
join(dir, "api.component.ts"),
|
|
54
|
+
`export const api = { name: "api", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "cfn-deploy", template: "dist/api.template.json" }] }] };\n`,
|
|
55
|
+
);
|
|
56
|
+
writeFileSync(join(dir, ".gitignore"), "dist/\n");
|
|
57
|
+
writeFileSync(join(dir, "marker.txt"), "one");
|
|
58
|
+
git(dir, "add", ".");
|
|
59
|
+
git(dir, "commit", "-q", "-m", "one (#11)");
|
|
60
|
+
const one = git(dir, "rev-parse", "HEAD");
|
|
61
|
+
writeFileSync(join(dir, "marker.txt"), "two");
|
|
62
|
+
git(dir, "commit", "-q", "-am", "two (#12)");
|
|
63
|
+
const two = git(dir, "rev-parse", "HEAD");
|
|
64
|
+
git(dir, "remote", "add", "origin", remote);
|
|
65
|
+
git(dir, "push", "-q", "origin", "main");
|
|
66
|
+
git(dir, "fetch", "-q", "origin");
|
|
67
|
+
git(dir, "tag", `ci/green/${one}`);
|
|
68
|
+
// The checkout is at two, not green, with an uncommitted change.
|
|
69
|
+
writeFileSync(join(dir, "marker.txt"), "dirty");
|
|
70
|
+
|
|
71
|
+
const unknown = chant(dir, ["release", "cut", "v1", "--at", "nope", "--build", BUILD]);
|
|
72
|
+
expect(unknown.code).toBe(1);
|
|
73
|
+
expect(unknown.stderr).toContain("[release-at-unknown]");
|
|
74
|
+
const notGreen = chant(dir, ["release", "cut", "v1", "--at", two, "--build", BUILD]);
|
|
75
|
+
expect(notGreen.code).toBe(1);
|
|
76
|
+
expect(notGreen.stderr).toContain("[release-commit-not-green]");
|
|
77
|
+
|
|
78
|
+
const candidates = chant(dir, ["release", "candidates", "--json"]);
|
|
79
|
+
expect(candidates.code, candidates.stderr).toBe(0);
|
|
80
|
+
const doc = JSON.parse(candidates.stdout);
|
|
81
|
+
expectValid(doc);
|
|
82
|
+
expect(doc.candidates.map((c: { commit: string }) => c.commit)).toEqual([one]);
|
|
83
|
+
expect(doc.candidates[0]).toMatchObject({ newFor: ["dev", "prod"], cut: [] });
|
|
84
|
+
expect(doc.candidates[0].changes[0]).toMatchObject({ commit: one, pr: 11 });
|
|
85
|
+
expect(doc.pending.map((c: { commit: string }) => c.commit)).toEqual([two]);
|
|
86
|
+
|
|
87
|
+
const dry = chant(dir, ["release", "cut", "v1", "--at", one, "--build", BUILD, "--json"]);
|
|
88
|
+
expect(dry.code, dry.stderr).toBe(0);
|
|
89
|
+
expect(JSON.parse(dry.stdout)).toMatchObject({ dryRun: true, commit: one, at: { ref: one, commit: one } });
|
|
90
|
+
|
|
91
|
+
const cut = chant(dir, ["release", "cut", "v1", "--at", one, "--build", BUILD, "--yes", "--json"]);
|
|
92
|
+
expect(cut.code, cut.stderr).toBe(0);
|
|
93
|
+
const result = JSON.parse(cut.stdout);
|
|
94
|
+
expect(result.release.commit).toBe(one);
|
|
95
|
+
expect(result.tag).toEqual({ name: "release/v1", created: true });
|
|
96
|
+
expect(git(dir, "rev-parse", "release/v1^{commit}")).toBe(one);
|
|
97
|
+
|
|
98
|
+
// The recorded template is commit one's, built with the label, and the checkout is untouched.
|
|
99
|
+
const out = join(root, "export");
|
|
100
|
+
const exported = chant(dir, ["release", "export", "v1", "--env", "prod", "-o", out]);
|
|
101
|
+
expect(exported.code, exported.stderr).toBe(0);
|
|
102
|
+
expect(JSON.parse(readFileSync(join(out, "dist", "api.template.json"), "utf-8"))).toEqual({ env: "prod", label: "v1", marker: "one" });
|
|
103
|
+
expect(readFileSync(join(dir, "marker.txt"), "utf-8")).toBe("dirty");
|
|
104
|
+
expect(git(dir, "rev-parse", "HEAD")).toBe(two);
|
|
105
|
+
expect(git(dir, "worktree", "list").split("\n")).toHaveLength(1);
|
|
106
|
+
|
|
107
|
+
const after = JSON.parse(chant(dir, ["release", "candidates", "--json"]).stdout);
|
|
108
|
+
expect(after.candidates[0].cut).toEqual(["v1"]);
|
|
109
|
+
} finally {
|
|
110
|
+
rmSync(root, { recursive: true, force: true });
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
240_000,
|
|
114
|
+
);
|
|
115
|
+
});
|
|
@@ -199,10 +199,10 @@ describe("chant release publish with targets", () => {
|
|
|
199
199
|
}
|
|
200
200
|
expect(pub.stderr).toContain("npm notice token [redacted]");
|
|
201
201
|
|
|
202
|
-
// From a workstation the
|
|
202
|
+
// From a workstation the plan offers no provenance, and with no supply-chain group says nothing about it (#4168).
|
|
203
203
|
const asJson = await chant(dir, ["release", "publish", "v1", "--json"], { ...path, ...secrets });
|
|
204
204
|
expect(asJson.code, asJson.stderr).toBe(0);
|
|
205
|
-
expect(asJson.stderr).toContain("this publish runs without OIDC");
|
|
205
|
+
expect(asJson.stderr).not.toContain("this publish runs without OIDC");
|
|
206
206
|
const doc = JSON.parse(asJson.stdout);
|
|
207
207
|
expect(doc).toMatchObject({ dryRun: true, label: "v1", provenance: { oidc: false } });
|
|
208
208
|
expect(doc.targets.map((t: { kind: string }) => t.kind)).toEqual(["npm", "oci"]);
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
* (#4049), in a temp repo with a temp bare remote and throwaway ssh keys. A
|
|
4
4
|
* release cut signed with a key `.chant/allowed_signers` lists verifies, and a
|
|
5
5
|
* publish takes it; an unsigned one and one signed by a key the file does not
|
|
6
|
-
* list fail `release verify
|
|
7
|
-
*
|
|
6
|
+
* list fail `release verify`. With no release gate or verified-releases
|
|
7
|
+
* group a publish takes them and records their status (#4168);
|
|
8
|
+
* release-publish.test.ts covers the refusals under the group. Nothing is
|
|
9
|
+
* published: the member declares no target.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
import { describe, expect, test } from "vitest";
|
|
@@ -42,7 +44,7 @@ const BUILD = `mkdir -p dist && printf '{"env":"%s"}' "$CHANT_ENV" > dist/api.te
|
|
|
42
44
|
|
|
43
45
|
describe("chant release verify", () => {
|
|
44
46
|
test.skipIf(!hasSshKeygen)(
|
|
45
|
-
"passes for a signed release, fails for an unsigned one and for one signed by an unlisted key; a publish
|
|
47
|
+
"passes for a signed release, fails for an unsigned one and for one signed by an unlisted key; a publish with no security group takes both and records their status",
|
|
46
48
|
() => {
|
|
47
49
|
const root = mkdtempSync(join(tmpdir(), "chant-release-verify-"));
|
|
48
50
|
try {
|
|
@@ -89,10 +91,10 @@ describe("chant release verify", () => {
|
|
|
89
91
|
expect(unsigned.code).toBe(1);
|
|
90
92
|
expect(unsigned.stderr).toMatch(/v2.* does not verify: no cut of the release carries a signature \[release-unsigned\]/);
|
|
91
93
|
expect(JSON.parse(chant(dir, ["release", "verify", "v2", "--json"]).stdout)).toMatchObject({ status: "unsigned", error: { code: "release-unsigned" } });
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
expect(pubUnsigned.stderr).
|
|
95
|
-
expect(
|
|
94
|
+
// #4168: with no release gate or verified-releases group, a publish takes it and records it unsigned.
|
|
95
|
+
const pubUnsigned = chant(dir, ["release", "publish", "v2", "--json"]);
|
|
96
|
+
expect(pubUnsigned.code, pubUnsigned.stderr).toBe(0);
|
|
97
|
+
expect(JSON.parse(pubUnsigned.stdout)).toMatchObject({ signature: { status: "unsigned" }, warnings: [] });
|
|
96
98
|
|
|
97
99
|
// v3: signed by mallory, whom the signers file does not list.
|
|
98
100
|
next("v3");
|
|
@@ -101,9 +103,9 @@ describe("chant release verify", () => {
|
|
|
101
103
|
const unlisted = chant(dir, ["release", "verify", "v3"]);
|
|
102
104
|
expect(unlisted.code).toBe(1);
|
|
103
105
|
expect(unlisted.stderr).toMatch(/mallory has no key in \.chant\/allowed_signers at [0-9a-f]{12}.*\[release-signature-unverified\]/);
|
|
104
|
-
const pubUnlisted = chant(dir, ["release", "publish", "v3"]);
|
|
105
|
-
expect(pubUnlisted.code).toBe(
|
|
106
|
-
expect(pubUnlisted.
|
|
106
|
+
const pubUnlisted = chant(dir, ["release", "publish", "v3", "--json"]);
|
|
107
|
+
expect(pubUnlisted.code, pubUnlisted.stderr).toBe(0);
|
|
108
|
+
expect(JSON.parse(pubUnlisted.stdout)).toMatchObject({ signature: { status: "unverified" }, warnings: [] });
|
|
107
109
|
|
|
108
110
|
const missing = chant(dir, ["release", "verify", "v9"]);
|
|
109
111
|
expect(missing.code).toBe(1);
|
|
@@ -128,8 +128,8 @@ describe("chant release cut, publish and list", () => {
|
|
|
128
128
|
expect(result.release.releaseDigest).toBe(dryJson.releaseDigest);
|
|
129
129
|
expect(result.release.commit).toBe(sha);
|
|
130
130
|
expect(result.release.targets).toEqual([
|
|
131
|
-
{ env: "dev", params: [], templates: [{ component: "api", path: "dist/api.template.json", digest: contentDigest(`{"env":"dev"}`) }] },
|
|
132
|
-
{ env: "prod", params: [], templates: [{ component: "api", path: "dist/api.template.json", digest: contentDigest(`{"env":"prod"}`) }] },
|
|
131
|
+
{ env: "dev", params: [], templates: [{ component: "api", path: "dist/api.template.json", digest: contentDigest(`{"env":"dev"}`) }], trees: [] },
|
|
132
|
+
{ env: "prod", params: [], templates: [{ component: "api", path: "dist/api.template.json", digest: contentDigest(`{"env":"prod"}`) }], trees: [] },
|
|
133
133
|
]);
|
|
134
134
|
expect(result.cut).toMatchObject({ actor: "t@chant.dev", runId: expect.stringMatching(/^local-\d+$/), runOrigin: { forge: "local" }, signature: null });
|
|
135
135
|
expect(result.next).toEqual(["git push origin release/v1", "chant release publish v1", "chant components promote --release v1 --to dev"]);
|
|
@@ -151,41 +151,32 @@ describe("chant release cut, publish and list", () => {
|
|
|
151
151
|
expect(doc.releases[0]).toMatchObject({ releaseDigest: result.release.releaseDigest, label: "v1", environments: [] });
|
|
152
152
|
expect(doc.releases[0].cuts[0]).toMatchObject({ runId: result.cut.runId, runOrigin: { forge: "local" } });
|
|
153
153
|
|
|
154
|
-
// #
|
|
155
|
-
const unsignedPub = chant(dir, ["release", "publish", "v1"]);
|
|
156
|
-
expect(unsignedPub.code).toBe(1);
|
|
157
|
-
expect(unsignedPub.stderr).toMatch(/v1 has no signed cut.*\[release-unsigned\]/);
|
|
158
|
-
expect(unsignedPub.stderr).toContain("pass --allow-unsigned");
|
|
159
|
-
|
|
154
|
+
// #4168: the cut is not signed, and with no release gate or verified-releases group a publish takes it quietly.
|
|
160
155
|
// A publish is a dry run without --yes, as a cut is.
|
|
161
|
-
const dryPub = chant(dir, ["release", "publish", "v1"
|
|
156
|
+
const dryPub = chant(dir, ["release", "publish", "v1"]);
|
|
162
157
|
expect(dryPub.code, dryPub.stderr).toBe(0);
|
|
163
|
-
expect(dryPub.stderr).toContain("
|
|
158
|
+
expect(dryPub.stderr).not.toContain("release-unsigned");
|
|
159
|
+
expect(dryPub.stderr).not.toContain("no signed cut");
|
|
164
160
|
expect(dryPub.stdout).toContain("What would be published where:\n nothing: this member declares no publish target (release.publish in chant.config.ts)");
|
|
165
|
-
expect(dryPub.stderr).toContain("Dry run: nothing was published. Run again with --yes to publish: chant release publish v1 --
|
|
161
|
+
expect(dryPub.stderr).toContain("Dry run: nothing was published. Run again with --yes to publish: chant release publish v1 --yes");
|
|
166
162
|
|
|
167
|
-
// Publish from the workstation:
|
|
168
|
-
const pub = chant(dir, ["release", "publish", "v1", "--
|
|
163
|
+
// Publish from the workstation: with no supply-chain group there is no OIDC warning either.
|
|
164
|
+
const pub = chant(dir, ["release", "publish", "v1", "--yes"]);
|
|
169
165
|
expect(pub.code, pub.stderr).toBe(0);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
expect(warning).toBeGreaterThan(-1);
|
|
173
|
-
expect(nothing).toBeGreaterThan(warning);
|
|
166
|
+
expect(pub.stderr).not.toContain("without OIDC");
|
|
167
|
+
expect(pub.stderr).toContain("Nothing was published: this member declares no publish target (release.publish in chant.config.ts).");
|
|
174
168
|
expect(pub.stderr).toContain("chant components promote --release v1 --to dev");
|
|
175
169
|
|
|
176
|
-
//
|
|
170
|
+
// The --json document records what is missing: no signature and no OIDC provenance.
|
|
171
|
+
const asJson = chant(dir, ["release", "publish", "v1", "--json"]);
|
|
172
|
+
expect(asJson.code, asJson.stderr).toBe(0);
|
|
173
|
+
expect(JSON.parse(asJson.stdout)).toMatchObject({ dryRun: true, label: "v1", signature: { status: "unsigned" }, provenance: { oidc: false }, warnings: [], targets: [], published: [] });
|
|
174
|
+
|
|
175
|
+
// In GitHub Actions with id-token: write the provenance is OIDC.
|
|
177
176
|
const oidc = { GITHUB_ACTIONS: "true", ACTIONS_ID_TOKEN_REQUEST_URL: "https://token.example", ACTIONS_ID_TOKEN_REQUEST_TOKEN: "t" };
|
|
178
|
-
const fromCi = chant(dir, ["release", "publish", "v1", "--
|
|
177
|
+
const fromCi = chant(dir, ["release", "publish", "v1", "--json"], oidc);
|
|
179
178
|
expect(fromCi.code, fromCi.stderr).toBe(0);
|
|
180
|
-
expect(fromCi.
|
|
181
|
-
expect(JSON.parse(fromCi.stdout)).toMatchObject({
|
|
182
|
-
dryRun: true,
|
|
183
|
-
label: "v1",
|
|
184
|
-
provenance: { oidc: true, ci: "github" },
|
|
185
|
-
warnings: ["v1 has no signed cut; publishing it anyway under --allow-unsigned, so chant release verify v1 will fail for it"],
|
|
186
|
-
targets: [],
|
|
187
|
-
published: [],
|
|
188
|
-
});
|
|
179
|
+
expect(JSON.parse(fromCi.stdout)).toMatchObject({ dryRun: true, label: "v1", provenance: { oidc: true, ci: "github" }, warnings: [], targets: [], published: [] });
|
|
189
180
|
|
|
190
181
|
const unknown = chant(dir, ["release", "publish", "v9"]);
|
|
191
182
|
expect(unknown.code).toBe(1);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `chant release` (#4026, ws-104): the release is what graduates.
|
|
3
3
|
*
|
|
4
|
-
* release cut [<label>] [--yes] [--build <command>] [--param k=v]... [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json]
|
|
4
|
+
* release cut [<label>] [--at <sha>] [--yes] [--build <command>] [--param k=v]... [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json]
|
|
5
5
|
* a dry run of a cut of this member from the checkout's HEAD, which must be
|
|
6
6
|
* clean, on trunk and pushed, and green (#4027); `--yes` cuts, records,
|
|
7
7
|
* and tags the commit `release/<label>` locally;
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
* release verify <label|digest> [--json]
|
|
18
18
|
* check the release's signature offline against .chant/allowed_signers
|
|
19
19
|
* at the release's commit (#4049)
|
|
20
|
+
* release candidates [--env <env>] [--json]
|
|
21
|
+
* the green commits of trunk a release can be cut from, each with what it
|
|
22
|
+
* would ship, and what each environment runs (#4168 4.2)
|
|
20
23
|
* release list [--json]
|
|
21
24
|
* every release of this member with its targets, its cuts and where it runs;
|
|
22
25
|
* `--json` prints the read contract's `releases` document
|
|
@@ -39,13 +42,19 @@ import { parseParamFlags } from "../build-params-cli";
|
|
|
39
42
|
import type { CommandContext } from "../registry";
|
|
40
43
|
|
|
41
44
|
const USAGE =
|
|
42
|
-
"chant release cut [<label>] [--yes] [--build <command>] [--param <k=v>] [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json] | release publish <label|digest> [--yes] [--allow-unsigned] [--require-provenance] [--json] | release verify <label|digest> [--json] | release list [--json] | release export <label|digest> --env <env> [-o <dir>] [--json]";
|
|
45
|
+
"chant release cut [<label>] [--at <sha>] [--yes] [--build <command>] [--param <k=v>] [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json] | release publish <label|digest> [--yes] [--allow-unsigned] [--require-provenance] [--json] | release verify <label|digest> [--json] | release candidates [--env <env>] [--json] | release list [--json] | release export <label|digest> --env <env> [-o <dir>] [--json]";
|
|
43
46
|
|
|
44
47
|
/** The build inputs `release cut` and `release compile` share. */
|
|
45
|
-
function compileInputs(
|
|
48
|
+
function compileInputs(
|
|
49
|
+
ctx: CommandContext,
|
|
50
|
+
params: Record<string, string> | undefined,
|
|
51
|
+
projectDir: string = resolve("."),
|
|
52
|
+
releaseLabel?: string | null,
|
|
53
|
+
): import("../../components/release-compile").CompileInputs {
|
|
46
54
|
const { args } = ctx;
|
|
47
55
|
return {
|
|
48
|
-
projectDir
|
|
56
|
+
projectDir,
|
|
57
|
+
...(releaseLabel ? { releaseLabel } : {}),
|
|
49
58
|
...(args.buildCommand ? { buildCommand: args.buildCommand } : {}),
|
|
50
59
|
...(params ? { params } : {}),
|
|
51
60
|
...(args.paramsFile ? { paramsFile: args.paramsFile } : {}),
|
|
@@ -102,6 +111,11 @@ function describeActorSource(source: import("../../lifecycle/release-workstation
|
|
|
102
111
|
* would record, build and tag, and what publishes where. With `--yes` it
|
|
103
112
|
* cuts, signs with `--sign`, records the cut with its actor and run id,
|
|
104
113
|
* tags the commit locally and prints the next command.
|
|
114
|
+
*
|
|
115
|
+
* `--at <sha>` (#4168 4.2) cuts that commit instead of `HEAD`, from a
|
|
116
|
+
* temporary worktree (../../lifecycle/release-at.ts), so the checkout need
|
|
117
|
+
* not be clean or at the commit. The label reaches every compile and
|
|
118
|
+
* artifact build as `CHANT_RELEASE_LABEL` (decision 5).
|
|
105
119
|
*/
|
|
106
120
|
export async function runReleaseCut(ctx: CommandContext): Promise<number> {
|
|
107
121
|
const { args } = ctx;
|
|
@@ -124,6 +138,8 @@ export async function runReleaseCut(ctx: CommandContext): Promise<number> {
|
|
|
124
138
|
console.error(formatError({ message: `${message} [${code}]`, ...(fix ? { hint: fix } : {}) }));
|
|
125
139
|
return 1;
|
|
126
140
|
};
|
|
141
|
+
const at = await import("../../lifecycle/release-at");
|
|
142
|
+
let checkout: import("../../lifecycle/release-at").CommitCheckout | null = null;
|
|
127
143
|
|
|
128
144
|
try {
|
|
129
145
|
// The workstation checks come first, in the order #4027 lists them; the
|
|
@@ -139,9 +155,16 @@ export async function runReleaseCut(ctx: CommandContext): Promise<number> {
|
|
|
139
155
|
let trunk: import("../../lifecycle/release-workstation").Trunk | null = null;
|
|
140
156
|
let signers: readonly import("../../workspace/trust/trust-files").Signer[] = [];
|
|
141
157
|
let member: string | null = null;
|
|
158
|
+
if (args.at !== undefined && !top) {
|
|
159
|
+
return fail("not-a-git-repository", "--at names a commit, and this directory is not in a git repository");
|
|
160
|
+
}
|
|
142
161
|
if (top) {
|
|
143
|
-
|
|
144
|
-
|
|
162
|
+
if (args.at !== undefined) {
|
|
163
|
+
commit = at.resolveCutCommit(top, args.at);
|
|
164
|
+
} else {
|
|
165
|
+
commit = await getHeadCommit({ cwd });
|
|
166
|
+
ws.checkCleanTree(top, commit);
|
|
167
|
+
}
|
|
145
168
|
const { scopeSource } = await import("../../workspace/write-scope");
|
|
146
169
|
const { releaseRuleFrom } = await import("../../workspace/identity");
|
|
147
170
|
const source = scopeSource(cwd);
|
|
@@ -179,8 +202,11 @@ export async function runReleaseCut(ctx: CommandContext): Promise<number> {
|
|
|
179
202
|
const tag = label !== null && commit !== null && top ? { name: ws.releaseTagName(member, label), state: ws.checkReleaseTag(top, ws.releaseTagName(member, label), commit) } : null;
|
|
180
203
|
const ci = resolveCiRun(process.env);
|
|
181
204
|
const params = parseParamFlags(args.param);
|
|
182
|
-
|
|
183
|
-
|
|
205
|
+
// --at: the cut reads, compiles and builds the commit's tree, not the checkout's.
|
|
206
|
+
if (args.at !== undefined && top && commit) checkout = at.checkoutCommit(top, cwd, commit);
|
|
207
|
+
const cutCwd = checkout?.memberDir ?? cwd;
|
|
208
|
+
const compiler = spawnTargetCompiler(compileInputs(ctx, params, cutCwd, label));
|
|
209
|
+
const request = { cwd: cutCwd, compiler, label, actor, ...(args.sign !== undefined ? { sign: args.sign } : {}) };
|
|
184
210
|
|
|
185
211
|
if (dryRun) {
|
|
186
212
|
const plan = await planRelease(request);
|
|
@@ -195,7 +221,7 @@ export async function runReleaseCut(ctx: CommandContext): Promise<number> {
|
|
|
195
221
|
const ref = plan.label ?? plan.alreadyCut ?? plan.releaseDigest;
|
|
196
222
|
const provenance = detectProvenance();
|
|
197
223
|
// The targets release publish would run, as the config here declares them; their files are read at publish.
|
|
198
|
-
const publishTargets = await releasePublishTargets(
|
|
224
|
+
const publishTargets = await releasePublishTargets(cutCwd).then(
|
|
199
225
|
(ts) => ts.map((t) => `${t.name} to ${t.destination}`),
|
|
200
226
|
(err: unknown) => (err instanceof Error ? err : new Error(String(err))),
|
|
201
227
|
);
|
|
@@ -207,6 +233,7 @@ export async function runReleaseCut(ctx: CommandContext): Promise<number> {
|
|
|
207
233
|
actorSource: who.source,
|
|
208
234
|
run: ci ? { runId: ci.runId, runOrigin: ci.runOrigin } : { runId: null, runOrigin: { forge: "local" as const } },
|
|
209
235
|
trunk,
|
|
236
|
+
at: args.at !== undefined ? { ref: args.at, commit } : null,
|
|
210
237
|
sign: args.sign !== undefined ? { key: publicKey !== null ? publicKey.split(" ")[0] : null, readable: publicKey !== null } : null,
|
|
211
238
|
tag: tag ? { name: tag.name, exists: tag.state === "present" } : null,
|
|
212
239
|
publish: { targets: Array.isArray(publishTargets) ? publishTargets : [], provenance },
|
|
@@ -294,7 +321,7 @@ export async function runReleaseCut(ctx: CommandContext): Promise<number> {
|
|
|
294
321
|
if (tagWarning) console.error(formatWarning({ message: tagWarning }));
|
|
295
322
|
return 0;
|
|
296
323
|
} catch (err) {
|
|
297
|
-
if (err instanceof ws.WorkstationCheckError || err instanceof ReleaseCutError) {
|
|
324
|
+
if (err instanceof ws.WorkstationCheckError || err instanceof ReleaseCutError || err instanceof at.CutAtError) {
|
|
298
325
|
return fail(err.code, err.message, err.fix);
|
|
299
326
|
}
|
|
300
327
|
if (err instanceof ReleaseLabelError) return fail(err.code, err.message);
|
|
@@ -303,6 +330,8 @@ export async function runReleaseCut(ctx: CommandContext): Promise<number> {
|
|
|
303
330
|
return 1;
|
|
304
331
|
}
|
|
305
332
|
throw err;
|
|
333
|
+
} finally {
|
|
334
|
+
checkout?.remove();
|
|
306
335
|
}
|
|
307
336
|
}
|
|
308
337
|
|
|
@@ -480,6 +509,47 @@ export async function runReleaseVerify(ctx: CommandContext): Promise<number> {
|
|
|
480
509
|
return 0;
|
|
481
510
|
}
|
|
482
511
|
|
|
512
|
+
/**
|
|
513
|
+
* `release candidates [--env <env>] [--json]` (#4168 4.2): the green commits
|
|
514
|
+
* of trunk a release can be cut from, newest first, each with what it would
|
|
515
|
+
* ship since the previous one, and what each environment runs. `--json`
|
|
516
|
+
* prints the read contract's `release-candidates` document.
|
|
517
|
+
*/
|
|
518
|
+
export async function runReleaseCandidates(ctx: CommandContext): Promise<number> {
|
|
519
|
+
const { releaseCandidatesDocument } = await import("../../workspace/release-candidates");
|
|
520
|
+
const doc = await releaseCandidatesDocument({ cwd: resolve("."), ...(ctx.args.env ? { env: ctx.args.env } : {}) });
|
|
521
|
+
if (ctx.args.json) {
|
|
522
|
+
console.log(JSON.stringify(doc, null, 2));
|
|
523
|
+
return "error" in doc ? 1 : 0;
|
|
524
|
+
}
|
|
525
|
+
if ("error" in doc) {
|
|
526
|
+
console.error(formatError({ message: `${doc.error.message} [${doc.error.code}]`, hint: USAGE }));
|
|
527
|
+
return 1;
|
|
528
|
+
}
|
|
529
|
+
for (const e of doc.environments) {
|
|
530
|
+
const runs = e.running.map((r) => `${r.label ?? r.releaseDigest.slice(0, 19)}${r.commit ? ` at ${r.commit.slice(0, 12)}` : ""}`).join(", ");
|
|
531
|
+
console.log(`${e.env}: ${runs || "runs no release yet"}`);
|
|
532
|
+
}
|
|
533
|
+
if (!doc.tip) {
|
|
534
|
+
console.error(`No candidates: this clone knows no ${doc.branch}. Fetch it: git fetch origin ${doc.branch} --tags`);
|
|
535
|
+
return 0;
|
|
536
|
+
}
|
|
537
|
+
if (doc.pending.length > 0) console.log(`${doc.pending.length} commit(s) on ${doc.branch} after the newest candidate are not green yet`);
|
|
538
|
+
if (doc.candidates.length === 0) console.log(`No green commit on ${doc.branch}${doc.since ? ` since ${doc.since.slice(0, 12)}` : ""} to cut from.`);
|
|
539
|
+
for (const c of doc.candidates) {
|
|
540
|
+
const tags = [...(c.newFor.length > 0 ? [`new for ${c.newFor.join(", ")}`] : []), ...(c.cut.length > 0 ? [`cut as ${c.cut.join(", ")}`] : [])];
|
|
541
|
+
console.log(`${c.commit.slice(0, 12)} ${c.subject}${tags.length > 0 ? ` (${tags.join("; ")})` : ""}`);
|
|
542
|
+
for (const ch of c.changes) {
|
|
543
|
+
const links = [...(ch.pr !== null ? [`#${ch.pr}`] : []), ...ch.work.map((w) => `work:${w}`)];
|
|
544
|
+
console.log(` ${ch.commit.slice(0, 12)} ${ch.subject}${links.length > 0 ? ` [${links.join(", ")}]` : ""}`);
|
|
545
|
+
}
|
|
546
|
+
for (const f of c.flags) console.log(` flag ${f.id} (${f.member} ${f.param}) changed`);
|
|
547
|
+
}
|
|
548
|
+
for (const sk of doc.skipped) console.log(`skipped ${sk.commit.slice(0, 12)} ${sk.subject} (revoked)`);
|
|
549
|
+
if (doc.candidates.length > 0) console.error(`Cut one: chant release cut <label> --at <sha>`);
|
|
550
|
+
return 0;
|
|
551
|
+
}
|
|
552
|
+
|
|
483
553
|
export async function runReleaseList(ctx: CommandContext): Promise<number> {
|
|
484
554
|
const { releasesDocument } = await import("../../workspace/releases");
|
|
485
555
|
const doc = await releasesDocument({ cwd: resolve(".") });
|
|
@@ -540,7 +610,7 @@ export async function runReleaseExport(ctx: CommandContext): Promise<number> {
|
|
|
540
610
|
console.error(formatError({ message: target.error }));
|
|
541
611
|
return 1;
|
|
542
612
|
}
|
|
543
|
-
const read = await targetFiles(found.release, target.target, (digest) => readReleaseTemplate(digest));
|
|
613
|
+
const read = await targetFiles(found.release, target.target, (digest, encoding) => readReleaseTemplate(digest, undefined, encoding));
|
|
544
614
|
if ("errors" in read) {
|
|
545
615
|
for (const e of read.errors) console.error(formatError({ message: e }));
|
|
546
616
|
return 1;
|