@intentius/chant 0.124.32 → 0.124.34
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/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/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +18 -3
- 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/pr.d.ts +2 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +124 -2
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +277 -74
- 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/main.d.ts.map +1 -1
- package/dist/cli/main.js +99 -22
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +30 -1
- package/dist/cli/registry.d.ts +21 -5
- package/dist/cli/registry.d.ts.map +1 -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.map +1 -1
- package/dist/components/driver.js +9 -0
- 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 +33 -10
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +42 -11
- package/dist/components/pr-run.d.ts +1 -1
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/promote-rounds.d.ts +102 -0
- package/dist/components/promote-rounds.d.ts.map +1 -0
- package/dist/components/promote-rounds.js +174 -0
- 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-gate.d.ts +30 -0
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +92 -42
- 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 +10 -0
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +32 -4
- package/dist/graph-declared.d.ts +4 -0
- package/dist/graph-declared.d.ts.map +1 -1
- package/dist/graph-declared.js +11 -1
- 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.d.ts +27 -0
- package/dist/lexicon.d.ts.map +1 -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-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/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/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/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/runtime.d.ts +2 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/pr-loop.d.ts +14 -3
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +15 -5
- package/dist/pr-report-url.d.ts +2 -2
- package/dist/pr-report-url.d.ts.map +1 -1
- 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/wave-runtime.mjs +236 -229
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.js +13 -2
- package/dist/workspace/check.schema.json +88 -2
- package/dist/workspace/checks/ci-workflow.d.ts +17 -0
- package/dist/workspace/checks/ci-workflow.d.ts.map +1 -0
- package/dist/workspace/checks/ci-workflow.js +123 -0
- package/dist/workspace/checks/security.d.ts +50 -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 +8 -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 +12 -0
- 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 +89 -4
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +310 -23
- package/dist/workspace/ci-init.d.ts +100 -0
- package/dist/workspace/ci-init.d.ts.map +1 -0
- package/dist/workspace/ci-init.js +434 -0
- package/dist/workspace/ci-last-green.schema.json +49 -0
- package/dist/workspace/ci-list.schema.json +35 -1
- package/dist/workspace/ci-run-index.d.ts +1 -1
- package/dist/workspace/ci-run-index.d.ts.map +1 -1
- package/dist/workspace/ci-run-index.schema.json +1 -1
- package/dist/workspace/ci-run.d.ts +12 -6
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +72 -16
- 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 +76 -7
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +153 -19
- package/dist/workspace/ci-workflow.d.ts +91 -0
- package/dist/workspace/ci-workflow.d.ts.map +1 -0
- package/dist/workspace/ci-workflow.js +33 -0
- package/dist/workspace/declaration.d.ts +35 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +12 -2
- package/dist/workspace/declaration.schema.json +51 -0
- package/dist/workspace/init.js +2 -2
- package/dist/workspace/ls.d.ts +2 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +1 -1
- package/dist/workspace/ls.schema.json +2 -1
- 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/pr-report.schema.json +3 -3
- package/dist/workspace/reason-codes.d.ts +26 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +28 -0
- 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/release-workflow.d.ts +31 -0
- package/dist/workspace/release-workflow.d.ts.map +1 -0
- package/dist/workspace/release-workflow.js +150 -0
- 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/package.json +8 -1
- package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +1 -1
- package/src/ci-jobs.ts +11 -2
- package/src/cli/handlers/build.test.ts +36 -0
- package/src/cli/handlers/build.ts +18 -3
- 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/pr-deploy-inputs.test.ts +209 -0
- package/src/cli/handlers/pr-record.test.ts +201 -0
- package/src/cli/handlers/pr.test.ts +22 -0
- package/src/cli/handlers/pr.ts +127 -4
- package/src/cli/handlers/promote.test.ts +17 -0
- package/src/cli/handlers/promote.ts +296 -84
- 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/main.ts +99 -22
- package/src/cli/parse-args.ts +22 -1
- package/src/cli/registry.ts +21 -5
- package/src/components/deploy-inputs.test.ts +108 -0
- package/src/components/deploy-inputs.ts +225 -0
- package/src/components/driver.ts +11 -0
- package/src/components/gate-plan.ts +9 -0
- package/src/components/pr-pipeline.ts +56 -15
- package/src/components/pr-run.ts +1 -1
- package/src/components/promote-rounds.test.ts +220 -0
- package/src/components/promote-rounds.ts +232 -0
- package/src/components/release-compile.test.ts +34 -0
- package/src/components/release-compile.ts +89 -8
- package/src/components/release-gate.test.ts +47 -0
- package/src/components/release-gate.ts +111 -46
- 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 +32 -4
- package/src/graph-declared-exports.test.ts +40 -0
- package/src/graph-declared.ts +9 -1
- 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.ts +41 -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-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/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/op/activities/activity-contracts.ts +3 -0
- 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/runtime.ts +2 -0
- package/src/op/step-console.test.ts +4 -2
- package/src/pr-loop.test.ts +29 -0
- package/src/pr-loop.ts +26 -7
- package/src/pr-report-url.ts +2 -2
- 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/workspace/__fixtures__/contract-repo.ts +11 -2
- package/src/workspace/check-contract.test.ts +4 -2
- package/src/workspace/check.schema.json +88 -2
- package/src/workspace/checks/ci-workflow.test.ts +150 -0
- package/src/workspace/checks/ci-workflow.ts +129 -0
- package/src/workspace/checks/security.test.ts +138 -0
- package/src/workspace/checks/security.ts +305 -0
- package/src/workspace/checks.ts +8 -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 +24 -0
- 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 +5 -2
- package/src/workspace/ci-green.ts +359 -27
- package/src/workspace/ci-init.test.ts +159 -0
- package/src/workspace/ci-init.ts +445 -0
- package/src/workspace/ci-last-green.schema.json +49 -0
- package/src/workspace/ci-list.schema.json +35 -1
- package/src/workspace/ci-report-url.test.ts +6 -1
- package/src/workspace/ci-run-index.schema.json +1 -1
- package/src/workspace/ci-run-index.ts +1 -1
- package/src/workspace/ci-run.test.ts +24 -7
- package/src/workspace/ci-run.ts +78 -16
- 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 +59 -0
- package/src/workspace/ci-serve.ts +177 -22
- package/src/workspace/ci-workflow.ts +71 -0
- package/src/workspace/declaration.schema.json +51 -0
- package/src/workspace/declaration.test.ts +17 -0
- package/src/workspace/declaration.ts +53 -3
- package/src/workspace/init.ts +2 -2
- package/src/workspace/ls.schema.json +2 -1
- package/src/workspace/ls.ts +3 -2
- package/src/workspace/member-pipeline.ts +2 -1
- package/src/workspace/merge-record.schema.json +49 -0
- package/src/workspace/pr-report.schema.json +3 -3
- package/src/workspace/reason-codes.test.ts +4 -0
- package/src/workspace/reason-codes.ts +28 -0
- 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/release-workflow.ts +167 -0
- package/src/workspace/security.test.ts +70 -0
- package/src/workspace/security.ts +118 -0
|
@@ -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;
|
package/src/cli/main.ts
CHANGED
|
@@ -249,22 +249,28 @@ CI:
|
|
|
249
249
|
chant/lifecycle; with ci.green.signedSince, the ones a
|
|
250
250
|
runner key signed. Pushes the
|
|
251
251
|
tags. --key, or the PEM in CHANT_RUNNER_KEY, signs
|
|
252
|
-
each tag with a runner key.
|
|
252
|
+
each tag with a runner key. A cancelled run leaves a
|
|
253
|
+
commit unbuilt, not red, and a failed run is run
|
|
254
|
+
again once. A phase with build: newest leaves the
|
|
255
|
+
commits a newer run superseded unbuilt, and a red
|
|
256
|
+
one starts a bisect: a ci/build/<sha> tag the tick
|
|
257
|
+
starts the phase's workflow at. Each commit judged
|
|
258
|
+
gets a _ci/<sha>.json record on chant/lifecycle.
|
|
253
259
|
ci run plan|apply --env <env> --base <sha> [--pr <n>] [--commit <sha>]
|
|
254
260
|
[--forge github|gitlab|forgejo] [--gate <name>] [--wave-gate <name>]
|
|
255
261
|
[--branch <name>] [--events-json] [--report-url <url>] [--image <image>]
|
|
256
262
|
[--before-script <line>]... [--pr-build]
|
|
257
263
|
[--pr-build-command <line>] [--chant <command>]
|
|
258
264
|
[--pass-env <NAME>]... [--secrets <file>] [--key <runner key.pem>]
|
|
259
|
-
[-o <dir>] [--wave <k>] [--
|
|
265
|
+
[-o <dir>] [--wave <k>] [--require-review]
|
|
260
266
|
[--no-status] [--status-url <url template>]
|
|
261
|
-
[--in-place | --local] [--no-push] [--verbose]
|
|
262
|
-
Run the pull-request pipeline's plan or apply job as
|
|
267
|
+
[--in-place | --local] [--no-push] [--require-runner-start] [--verbose]
|
|
268
|
+
Run the pull-request pipeline's plan, record or apply job as
|
|
263
269
|
a pipeline Op in a fresh worktree of --commit (default
|
|
264
270
|
HEAD) with a clean environment. --base and --pr stand
|
|
265
271
|
in for the forge's event values. --pr-build runs
|
|
266
272
|
the job's build and lint lines first. Records the run as
|
|
267
|
-
pr-plan or pr-apply on chant/lifecycle, which ci tick
|
|
273
|
+
pr-plan, pr-record or pr-apply on chant/lifecycle, which ci tick
|
|
268
274
|
from runs counts, signed when --key or CHANT_RUNNER_KEY
|
|
269
275
|
gives a runner key, and copies the report to -o
|
|
270
276
|
(default .chant/pr). --in-place runs in this checkout
|
|
@@ -286,17 +292,39 @@ CI:
|
|
|
286
292
|
kept on the local chant/lifecycle with runOrigin
|
|
287
293
|
local and is never pushed, no status is set, nothing
|
|
288
294
|
goes under a report URL, and ci tick does not count it.
|
|
295
|
+
--require-runner-start refuses the job before its
|
|
296
|
+
first step unless a runner-start statement, signed by
|
|
297
|
+
a runner key .chant/trust.json at --base lists, names
|
|
298
|
+
this forge job.
|
|
299
|
+
ci runner-start sign --key <runner key.pem> --principal <name> --job <json|@file> [-o <file>]
|
|
300
|
+
For the provisioner that starts a runner for one job:
|
|
301
|
+
sign the job's system, repo, run, attempt, job,
|
|
302
|
+
commit and runner name, and print the envelope.
|
|
303
|
+
ci runner-start verify [--base <rev>] [--commit <sha>] [--json]
|
|
304
|
+
In the job: check the statement in CHANT_RUNNER_START,
|
|
305
|
+
or the file CHANT_RUNNER_START_FILE names (default
|
|
306
|
+
/run/chant/runner-start.json), against the forge's
|
|
307
|
+
variables and .chant/trust.json at --base (default
|
|
308
|
+
origin/HEAD). Exits 1 unless it verifies.
|
|
309
|
+
ci run --at <sha> Ask for every required phase at a commit of the
|
|
310
|
+
green branch with a ci/build/<sha> tag, and start the
|
|
311
|
+
workflows holding them there on GitHub when a token
|
|
312
|
+
is set; otherwise the next tick, or ci serve
|
|
313
|
+
--build-command, does.
|
|
289
314
|
ci runs [--report-url <url>] [--json]
|
|
290
315
|
List the runs in the index at the report URL (default
|
|
291
316
|
the workspace's ci.reportUrl), newest first.
|
|
292
317
|
ci serve --forge forgejo|github --repo <owner/repo> --env <env>
|
|
293
318
|
[--interval <s>] [--max-jobs <n>] [--state <dir>]
|
|
294
319
|
[--author <login>]... [--once] [--rerun-failed] [--unsigned]
|
|
295
|
-
[ci run's job flags]
|
|
320
|
+
[--apply-on-merge] [--build-command <line>] [ci run's job flags]
|
|
296
321
|
Poll the forge for open pull requests and the target
|
|
297
322
|
branch's head, and run ci run plan for each new pull
|
|
298
|
-
request head and ci run
|
|
299
|
-
the branch
|
|
323
|
+
request head and ci run record for each new commit on
|
|
324
|
+
the branch, then ci tick from the runs when the
|
|
325
|
+
workspace declares ci.green. A merge deploys nothing;
|
|
326
|
+
--apply-on-merge runs ci run apply in place of the
|
|
327
|
+
record, and --wave-gate needs it. Dedupes on job and commit in a state file
|
|
300
328
|
under --state, takes a lease per apply group, keeps
|
|
301
329
|
each apply's resume record there, and runs at most
|
|
302
330
|
--max-jobs (default 1) jobs at once. --once runs one
|
|
@@ -304,13 +332,22 @@ CI:
|
|
|
304
332
|
failed apply or plan again. Signs runs with a runner
|
|
305
333
|
key when .chant/trust.json at the target's head lists
|
|
306
334
|
it, and serves unsigned otherwise; with
|
|
307
|
-
ci.green.signedSince it refuses to start without one
|
|
335
|
+
ci.green.signedSince it refuses to start without one.
|
|
336
|
+
--build-command runs <line> once per ci/build/<sha>
|
|
337
|
+
tag, in a fresh worktree of the commit, with
|
|
338
|
+
CHANT_BUILD_COMMIT and CHANT_BUILD_PHASES set
|
|
308
339
|
ci serve|run --setup-key [--trust [--principal <name>]] [--state <dir>] [--branch <name>]
|
|
309
340
|
Make the host's runner key (Ed25519, mode 0600) in the
|
|
310
341
|
state directory when there is none and print its public
|
|
311
342
|
key. --trust commits its runner entry on a branch of its
|
|
312
343
|
own and pushes it for review; the target branch is never
|
|
313
344
|
written
|
|
345
|
+
ci init --workflow trunk-curated|trunk-curated+apply-on-merge
|
|
346
|
+
[--forge github|gitlab|forgejo] [--security <group>]... [--yes]
|
|
347
|
+
Set the workspace up for a prescribed delivery workflow:
|
|
348
|
+
print what it would write as a diff, then write it on
|
|
349
|
+
--yes or a yes at the prompt and run the generators. A
|
|
350
|
+
rerun proposes only what workspace check still reports
|
|
314
351
|
ci workflow [--workflow <name>]... [--output <file>] Write
|
|
315
352
|
[--chant <command>] [--install <command>]
|
|
316
353
|
[--token-secret <NAME> | --app-id-var <VAR> --app-key-secret <NAME>]
|
|
@@ -322,10 +359,14 @@ CI:
|
|
|
322
359
|
or a GitHub App's id and key, pushes the tags with a
|
|
323
360
|
token that may tag commits changing workflow files.
|
|
324
361
|
--runner-key-secret hands the tick the runner key it
|
|
325
|
-
signs its tags with.
|
|
362
|
+
signs its tags with. For each phase that sets build,
|
|
363
|
+
it also writes the triggers and concurrency of the
|
|
364
|
+
workflows holding it: newest builds on pushes and by
|
|
365
|
+
hand, one run at a time, and every gives each commit
|
|
366
|
+
a run of its own.
|
|
326
367
|
|
|
327
368
|
Releases:
|
|
328
|
-
release cut [<label>] [--yes] [--build <command>] [--param <k=v>]
|
|
369
|
+
release cut [<label>] [--at <sha>] [--yes] [--build <command>] [--param <k=v>]
|
|
329
370
|
[--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>]
|
|
330
371
|
[--actor <name>] [--sign [<key file>]] [--json]
|
|
331
372
|
A dry run of a cut of this member from HEAD, which
|
|
@@ -344,22 +385,37 @@ Releases:
|
|
|
344
385
|
requires it. A project with no components releases
|
|
345
386
|
what chant build --env <env> writes: --src <dir> is
|
|
346
387
|
the build root, -o <file> the output (dist/build.yaml),
|
|
347
|
-
--lexicon narrows it.
|
|
388
|
+
--lexicon narrows it. --at <sha> cuts that commit of
|
|
389
|
+
trunk from a temporary worktree instead of HEAD, so
|
|
390
|
+
the checkout need not be clean. The label reaches
|
|
391
|
+
every compile and build as CHANT_RELEASE_LABEL.
|
|
392
|
+
release candidates [--env <env>] [--json]
|
|
393
|
+
The green commits of trunk a release can be cut from,
|
|
394
|
+
newest first, each with the commits, work items, pull
|
|
395
|
+
requests and flags it would ship, and what each
|
|
396
|
+
environment runs.
|
|
348
397
|
release publish <label|digest> [--yes] [--allow-unsigned]
|
|
349
398
|
[--require-provenance] [--json]
|
|
350
399
|
Publish a cut release to the targets release.publish
|
|
351
400
|
in chant.config.ts declares (npm, OCI registries,
|
|
352
|
-
forge release assets), each with
|
|
353
|
-
|
|
354
|
-
Refuses a revoked release,
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
401
|
+
forge release assets), each with the release's
|
|
402
|
+
provenance and an SBOM when it has a source. A dry
|
|
403
|
+
run without --yes. Refuses a revoked release, and
|
|
404
|
+
with a target a checkout that is not the release's
|
|
405
|
+
commit. Under security verified-releases it refuses
|
|
406
|
+
one whose signature does not verify against
|
|
407
|
+
.chant/allowed_signers unless --allow-unsigned; under
|
|
408
|
+
supply-chain it warns when the run has no OIDC token
|
|
409
|
+
and refuses a target with no SBOM source.
|
|
410
|
+
--require-provenance refuses a run with no OIDC.
|
|
360
411
|
release verify <label|digest> [--json]
|
|
361
412
|
Check, offline, that the release was signed by a key
|
|
362
413
|
.chant/allowed_signers lists at the release's commit.
|
|
414
|
+
release workflow [--forge github|gitlab|forgejo] [--install <command>]
|
|
415
|
+
[--build <command>] [--chant <command>] [--output <file>]
|
|
416
|
+
Write the publish workflow, run on a release/** tag.
|
|
417
|
+
Under supply-chain it passes --require-provenance, and
|
|
418
|
+
under verified-releases it runs release verify first
|
|
363
419
|
release list [--json] Every release of this member with its targets, its
|
|
364
420
|
cuts and the environments that run it.
|
|
365
421
|
release export <label|digest> --env <env> [-o <dir>] [--json]
|
|
@@ -887,6 +943,10 @@ Lifecycle (alias: lc):
|
|
|
887
943
|
estate size rather than stack count (#2405)
|
|
888
944
|
lifecycle affected Stacks a change affects (--base <ref> [--include-dependents])
|
|
889
945
|
--json: emit the ChangeSet as JSON
|
|
946
|
+
lifecycle health <env> Whether each declared service is up, separate from
|
|
947
|
+
drift: ECS running vs desired, Route 53 health
|
|
948
|
+
checks, Container Apps revisions, Cloud Run Ready.
|
|
949
|
+
A failed read is "not read", never unhealthy (--json)
|
|
890
950
|
lifecycle whoami <env> Who chant would act as in each configured lexicon,
|
|
891
951
|
and what that principal is scoped to — read-only,
|
|
892
952
|
before anything acts (--json, --strict)
|
|
@@ -931,7 +991,12 @@ Component release ledger + status:
|
|
|
931
991
|
report and note (--base <ref> --pr <n>
|
|
932
992
|
[--forge github|gitlab|forgejo] posts them);
|
|
933
993
|
applies nothing
|
|
934
|
-
components pr-
|
|
994
|
+
components pr-record On merge (#4168): plan the same members again,
|
|
995
|
+
store the change set and write the merge record
|
|
996
|
+
_merges/<commit>.json; applies and refuses
|
|
997
|
+
nothing, and a plan that differs from the
|
|
998
|
+
approved one sets a failing chant/record status
|
|
999
|
+
components pr-apply With --apply-on-merge: plan the same members again and apply
|
|
935
1000
|
them only if an approval stands for that digest
|
|
936
1001
|
(chant approve pr-<n> pr-apply --plan <digest>);
|
|
937
1002
|
a moved plan refuses with exit 3
|
|
@@ -968,7 +1033,11 @@ Component release ledger + status:
|
|
|
968
1033
|
[--component <name> [--digest <sha256:...>]];
|
|
969
1034
|
--from defaults to the environment before --to
|
|
970
1035
|
in the config's environments; --dry-run prints
|
|
971
|
-
the plan; the target's gates still apply
|
|
1036
|
+
the plan; the target's gates still apply. Under
|
|
1037
|
+
--release a dependent plans against its
|
|
1038
|
+
upstream's planned outputs and plans again once
|
|
1039
|
+
it applies; --wave-gate <gate> checks each wave
|
|
1040
|
+
at <gate>-wave-<k>; a stop writes --resume <file>)
|
|
972
1041
|
components rollback <env> Redeploy an earlier release of an environment
|
|
973
1042
|
from its recorded digest (--component <name>
|
|
974
1043
|
[--digest <sha256:...>]; defaults to the release
|
|
@@ -1383,9 +1452,12 @@ export const commandRegistry: CommandDef[] = [
|
|
|
1383
1452
|
{ name: "ci list", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-green-cli")).runCiList(ctx) },
|
|
1384
1453
|
{ name: "ci tick", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-green-cli")).runCiTick(ctx) },
|
|
1385
1454
|
{ name: "ci workflow", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-green-cli")).runCiWorkflow(ctx) },
|
|
1455
|
+
{ name: "ci init", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-init")).runCiInit(ctx) },
|
|
1386
1456
|
// #4094: one pull-request job as a pipeline Op in a fresh checkout, the chant runner's first slice.
|
|
1387
1457
|
{ name: "ci run", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-run")).runCiRun(ctx) },
|
|
1388
1458
|
// #4098: the run index at the report URL.
|
|
1459
|
+
// #4147: a provisioner's signed word that it started a runner for one job, signed and checked.
|
|
1460
|
+
{ name: "ci runner-start", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-runner-start")).runCiRunnerStart(ctx) },
|
|
1389
1461
|
{ name: "ci runs", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-run-index")).runCiRuns(ctx) },
|
|
1390
1462
|
{ name: "ci serve", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-serve")).runCiServe(ctx) },
|
|
1391
1463
|
// #4026: the release cut from a green commit (ws-104). Imported on first use.
|
|
@@ -1394,6 +1466,8 @@ export const commandRegistry: CommandDef[] = [
|
|
|
1394
1466
|
{ name: "release publish", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleasePublish(ctx) },
|
|
1395
1467
|
// #4049: check a release's signature offline.
|
|
1396
1468
|
{ name: "release verify", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseVerify(ctx) },
|
|
1469
|
+
{ name: "release workflow", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/release-workflow")).runReleaseWorkflow(ctx) },
|
|
1470
|
+
{ name: "release candidates", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseCandidates(ctx) },
|
|
1397
1471
|
{ name: "release list", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseList(ctx) },
|
|
1398
1472
|
{ name: "release export", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseExport(ctx) },
|
|
1399
1473
|
{ name: "release compile", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseCompile(ctx) },
|
|
@@ -1504,6 +1578,8 @@ export const commandRegistry: CommandDef[] = [
|
|
|
1504
1578
|
{ name: "lifecycle plan", requiresPlugins: true, handler: runLifecyclePlan },
|
|
1505
1579
|
{ name: "lifecycle affected", requiresPlugins: true, handler: runLifecycleAffected },
|
|
1506
1580
|
{ name: "lifecycle whoami", requiresPlugins: true, handler: runLifecycleWhoami },
|
|
1581
|
+
// #4177 — runtime health, separate from drift. Imported on first use.
|
|
1582
|
+
{ name: "lifecycle health", requiresPlugins: true, handler: async (ctx) => (await import("./handlers/lifecycle-health")).runLifecycleHealth(ctx) },
|
|
1507
1583
|
{ name: "lifecycle teardown", requiresPlugins: true, handler: runLifecycleTeardown },
|
|
1508
1584
|
{ name: "lifecycle log", handler: runLifecycleLog },
|
|
1509
1585
|
// #4036 — retention's garbage collection. Imported on first use.
|
|
@@ -1519,6 +1595,7 @@ export const commandRegistry: CommandDef[] = [
|
|
|
1519
1595
|
// #3183 — the pull-request loop the generated CI runs. Imported on first use.
|
|
1520
1596
|
{ name: "components pr-plan", requiresPlugins: true, handler: async (ctx) => (await import("./handlers/pr")).runComponentsPrPlan(ctx) },
|
|
1521
1597
|
{ name: "components pr-apply", requiresPlugins: true, handler: async (ctx) => (await import("./handlers/pr")).runComponentsPrApply(ctx) },
|
|
1598
|
+
{ name: "components pr-record", requiresPlugins: true, handler: async (ctx) => (await import("./handlers/pr")).runComponentsPrRecord(ctx) },
|
|
1522
1599
|
{ name: "components status", requiresPlugins: true, handler: runComponentsStatus },
|
|
1523
1600
|
{ name: "components release", handler: runComponentsReleaseRecord },
|
|
1524
1601
|
{ name: "components export", handler: runComponentsExport },
|
package/src/cli/parse-args.ts
CHANGED
|
@@ -70,6 +70,7 @@ const BOOLEAN_FLAGS = new Set([
|
|
|
70
70
|
"--trust",
|
|
71
71
|
"--unsigned",
|
|
72
72
|
"--in-place",
|
|
73
|
+
"--require-runner-start",
|
|
73
74
|
"--no-push",
|
|
74
75
|
"--no-require-review",
|
|
75
76
|
"--expire",
|
|
@@ -409,9 +410,17 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
409
410
|
result.requireProvenance = true;
|
|
410
411
|
} else if (arg === "--pr-loop") {
|
|
411
412
|
result.prLoop = true;
|
|
413
|
+
} else if (arg === "--apply-on-merge") {
|
|
414
|
+
// `chant build --pr-loop --apply-on-merge` and `chant ci serve --apply-on-merge` (#4168): opt into applying on merge.
|
|
415
|
+
result.applyOnMerge = true;
|
|
412
416
|
} else if (arg === "--pr-build") {
|
|
413
417
|
// `chant build --pr-loop --pr-build` (#4088), and `chant ci run --pr-build` (#4094).
|
|
414
418
|
result.prBuild = true;
|
|
419
|
+
} else if (arg === "--build-command") {
|
|
420
|
+
// `chant ci serve --build-command <line>` (#4169): the line that builds a requested commit.
|
|
421
|
+
const line = args[++i];
|
|
422
|
+
if (line === undefined || line === "") throw new Error("--build-command needs a shell line: --build-command '<line>'");
|
|
423
|
+
result.ciBuildCommand = line;
|
|
415
424
|
} else if (arg === "--pr-build-command") {
|
|
416
425
|
const line = args[++i];
|
|
417
426
|
if (line === undefined || line === "") throw new Error("--pr-build-command needs a shell line: --pr-build-command '<line>'");
|
|
@@ -531,6 +540,11 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
531
540
|
// `chant workspace wip --branch <branch>` (#3172)
|
|
532
541
|
result.branch = args[++i];
|
|
533
542
|
if (!result.branch || result.branch.startsWith("-")) throw new Error("--branch needs a branch name: --branch <branch>");
|
|
543
|
+
} else if (arg === "--security") {
|
|
544
|
+
// `chant ci init --security <group>`, repeatable (#4168 step 6): a security group to turn on.
|
|
545
|
+
const group = args[++i];
|
|
546
|
+
if (!group || group.startsWith("-")) throw new Error("--security needs a group: --security verified-releases|supply-chain|who-ran-it|review-rules|isolated-runners");
|
|
547
|
+
result.securityGroups = [...(result.securityGroups ?? []), group];
|
|
534
548
|
} else if (arg === "--workflow") {
|
|
535
549
|
// `chant ci workflow --workflow <name>`, repeatable (#3573)
|
|
536
550
|
const name = args[++i];
|
|
@@ -783,11 +797,18 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
783
797
|
} else if (arg === "--in-place") {
|
|
784
798
|
// `chant ci run --in-place` (#4156): run the job in this checkout, as a generated forge job does.
|
|
785
799
|
result.ciInPlace = true;
|
|
800
|
+
} else if (arg === "--require-runner-start") {
|
|
801
|
+
// `chant ci run --require-runner-start` (#4147): refuse the job unless a provisioner's runner-start statement names it.
|
|
802
|
+
result.requireRunnerStart = true;
|
|
803
|
+
} else if (arg === "--job") {
|
|
804
|
+
// `chant ci runner-start sign --job <json|@file>` (#4147): the CI job a runner was started for.
|
|
805
|
+
result.ciJob = args[++i];
|
|
806
|
+
if (!result.ciJob || result.ciJob.startsWith("-")) throw new Error("--job needs the job as JSON or @<file>: --job @job.json");
|
|
786
807
|
} else if (arg === "--no-push") {
|
|
787
808
|
// `chant ci run --no-push` (#4156): keep the run record on the local chant/lifecycle.
|
|
788
809
|
result.noPush = true;
|
|
789
810
|
} else if (arg === "--no-require-review") {
|
|
790
|
-
// `chant ci run apply --no-require-review` (#4156): the apply without pr-apply's --require-review.
|
|
811
|
+
// `chant ci run apply --no-require-review` (#4156): the apply without pr-apply's --require-review, the default since #4168.
|
|
791
812
|
result.requireReview = false;
|
|
792
813
|
} else if (arg === "--unsigned") {
|
|
793
814
|
// `chant ci serve --unsigned` (#4099): serve without a runner key listed at the target's head.
|