@intentius/chant 0.124.30 → 0.124.32
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/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 +23 -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/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 +14 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +488 -32
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +3 -1
- 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 +74 -12
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +65 -0
- package/dist/cli/registry.d.ts +29 -2
- 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/driver.d.ts +38 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +47 -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/pr-pipeline.d.ts +65 -2
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +107 -15
- package/dist/components/pr-run.d.ts +127 -29
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +290 -70
- package/dist/components/wave-plan.d.ts +5 -2
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +12 -1
- 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/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 +2 -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/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/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/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/factory.js +1 -1
- 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/local-executor.d.ts +12 -0
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +22 -3
- 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/redact.d.ts +34 -0
- package/dist/op/redact.d.ts.map +1 -0
- package/dist/op/redact.js +94 -0
- package/dist/op/step-console.d.ts +2 -0
- package/dist/op/step-console.d.ts.map +1 -1
- package/dist/op/step-console.js +5 -2
- 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 +27 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +51 -6
- package/dist/pr-loop.d.ts +141 -4
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +122 -6
- package/dist/pr-report-url.d.ts +10 -2
- package/dist/pr-report-url.d.ts.map +1 -1
- package/dist/pr-report-url.js +52 -6
- 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 +208 -208
- 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/checks/boxes.d.ts +6 -6
- package/dist/workspace/checks/boxes.js +9 -9
- package/dist/workspace/ci-green-forge.d.ts +6 -4
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green.d.ts +28 -12
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +49 -21
- package/dist/workspace/ci-list.schema.json +12 -6
- 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 +108 -0
- package/dist/workspace/ci-run-index.d.ts.map +1 -0
- package/dist/workspace/ci-run-index.js +164 -0
- package/dist/workspace/ci-run-index.schema.json +52 -0
- package/dist/workspace/ci-run.d.ts +159 -9
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +419 -50
- package/dist/workspace/ci-runner-key.d.ts +133 -0
- package/dist/workspace/ci-runner-key.d.ts.map +1 -0
- package/dist/workspace/ci-runner-key.js +237 -0
- package/dist/workspace/ci-secrets.d.ts +65 -0
- package/dist/workspace/ci-secrets.d.ts.map +1 -0
- package/dist/workspace/ci-secrets.js +211 -0
- package/dist/workspace/ci-serve.d.ts +67 -4
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +240 -35
- package/dist/workspace/declaration.d.ts +8 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +11 -2
- package/dist/workspace/declaration.schema.json +7 -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 +6 -1
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +1 -0
- package/dist/workspace/ls.schema.json +7 -1
- 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 +74 -7
- package/dist/workspace/reason-codes.d.ts +7 -3
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +9 -4
- package/dist/workspace/run-event.schema.json +3 -1
- package/dist/workspace/status.schema.json +3 -1
- package/dist/workspace/trust/trust-write.d.ts +109 -0
- package/dist/workspace/trust/trust-write.d.ts.map +1 -0
- package/dist/workspace/trust/trust-write.js +320 -0
- package/dist/workspace/trust-write.schema.json +265 -0
- 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 +1 -1
- package/src/__fixtures__/pr-loop/pr-apply-wave-waiting.note.golden.md +48 -0
- 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 +69 -0
- package/src/cli/handlers/build.ts +20 -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/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-waves.test.ts +449 -0
- package/src/cli/handlers/pr.test.ts +134 -0
- package/src/cli/handlers/pr.ts +523 -31
- package/src/cli/handlers/promote.test.ts +26 -1
- package/src/cli/handlers/promote.ts +3 -2
- 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 +19 -0
- package/src/cli/main.ts +73 -12
- package/src/cli/parse-args.ts +48 -0
- package/src/cli/registry.ts +35 -4
- 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/driver.test.ts +36 -1
- package/src/components/driver.ts +76 -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/pr-pipeline.test.ts +100 -0
- package/src/components/pr-pipeline.ts +132 -17
- package/src/components/pr-run.test.ts +323 -18
- package/src/components/pr-run.ts +354 -75
- package/src/components/wave-plan.ts +16 -4
- 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/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 +2 -0
- package/src/lifecycle/affected.test.ts +7 -0
- package/src/lifecycle/affected.ts +6 -1
- package/src/lifecycle/release-ledger.ts +34 -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/module-source.test.ts +85 -0
- package/src/module-source.ts +112 -0
- package/src/op/activities/factory.ts +1 -1
- 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/local-executor.ts +31 -3
- 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/redact.ts +93 -0
- package/src/op/step-console.ts +7 -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 +71 -0
- package/src/pr-forge.ts +70 -6
- package/src/pr-loop-bundle.test.ts +2 -1
- package/src/pr-loop.test.ts +117 -1
- package/src/pr-loop.ts +252 -10
- package/src/pr-report-url.ts +58 -6
- 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/checks/boxes.ts +9 -9
- package/src/workspace/ci-green-forge.ts +6 -4
- package/src/workspace/ci-green-runs.test.ts +53 -11
- package/src/workspace/ci-green.test.ts +1 -1
- package/src/workspace/ci-green.ts +50 -22
- package/src/workspace/ci-list.schema.json +12 -6
- 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 +52 -0
- package/src/workspace/ci-run-index.test.ts +392 -0
- package/src/workspace/ci-run-index.ts +206 -0
- package/src/workspace/ci-run.test.ts +520 -42
- package/src/workspace/ci-run.ts +483 -51
- package/src/workspace/ci-runner-key.test.ts +204 -0
- package/src/workspace/ci-runner-key.ts +275 -0
- package/src/workspace/ci-secrets.test.ts +106 -0
- package/src/workspace/ci-secrets.ts +213 -0
- package/src/workspace/ci-serve.test.ts +282 -39
- package/src/workspace/ci-serve.ts +231 -34
- package/src/workspace/declaration.schema.json +7 -2
- package/src/workspace/declaration.test.ts +23 -0
- package/src/workspace/declaration.ts +19 -3
- package/src/workspace/export.ts +6 -3
- package/src/workspace/ls.schema.json +7 -1
- package/src/workspace/ls.ts +7 -2
- package/src/workspace/pin-integrity.test.ts +5 -0
- package/src/workspace/pin-integrity.ts +3 -2
- package/src/workspace/pr-report.schema.json +74 -7
- package/src/workspace/reason-codes.test.ts +2 -0
- package/src/workspace/reason-codes.ts +9 -4
- package/src/workspace/run-event.schema.json +3 -1
- package/src/workspace/status.schema.json +3 -1
- package/src/workspace/trust/trust-write.test.ts +179 -0
- package/src/workspace/trust/trust-write.ts +377 -0
- package/src/workspace/trust-write.schema.json +265 -0
- package/src/workspace/write-scope.test.ts +29 -1
- package/src/workspace/write-scope.ts +12 -1
package/src/pr-forge.test.ts
CHANGED
|
@@ -85,6 +85,21 @@ describe("github", () => {
|
|
|
85
85
|
expect(calls[0].body).toEqual({ state: "success", context: "chant/plan", description: "2 members", target_url: "https://ci/run/1" });
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
+
test("the authors of a pull request are its opener and every commit's author and committer, every page read (#4000)", async () => {
|
|
89
|
+
const page1 = Array.from({ length: 100 }, () => ({ author: { login: "carol" }, committer: { login: "carol" } }));
|
|
90
|
+
const { fetch, calls } = fakeFetch({
|
|
91
|
+
"GET /repos/acme/infra/pulls/12": { number: 12, user: { login: "carol" } },
|
|
92
|
+
"GET /repos/acme/infra/pulls/12/commits?page=1": page1,
|
|
93
|
+
"GET /repos/acme/infra/pulls/12/commits?page=2": [{ author: null, committer: { login: "dave" } }, { author: { login: "erin" }, committer: { login: "web-flow" } }],
|
|
94
|
+
});
|
|
95
|
+
expect(await githubForge({ ...opts, fetch }).authors(12)).toEqual(["carol", "dave", "erin", "web-flow"]);
|
|
96
|
+
expect(calls.map((c) => c.url)).toEqual([
|
|
97
|
+
"https://api.github.com/repos/acme/infra/pulls/12",
|
|
98
|
+
"https://api.github.com/repos/acme/infra/pulls/12/commits?per_page=100&page=1",
|
|
99
|
+
"https://api.github.com/repos/acme/infra/pulls/12/commits?per_page=100&page=2",
|
|
100
|
+
]);
|
|
101
|
+
});
|
|
102
|
+
|
|
88
103
|
test("an approver is someone whose newest deciding review approves", async () => {
|
|
89
104
|
const { fetch } = fakeFetch({
|
|
90
105
|
"GET /repos/acme/infra/pulls/12/reviews": [
|
|
@@ -195,6 +210,15 @@ describe("forgejo", () => {
|
|
|
195
210
|
expect(calls[0].headers.Authorization).toBe("token t0k");
|
|
196
211
|
});
|
|
197
212
|
|
|
213
|
+
test("the authors of a pull request come from the same endpoints, paged by limit (#4000)", async () => {
|
|
214
|
+
const { fetch, calls } = fakeFetch({
|
|
215
|
+
"GET /api/v1/repos/acme/infra/pulls/4": { number: 4, user: { login: "ana" } },
|
|
216
|
+
"GET /api/v1/repos/acme/infra/pulls/4/commits": [{ author: { login: "ana" }, committer: { login: "ben" } }],
|
|
217
|
+
});
|
|
218
|
+
expect(await forgejoForge({ ...opts, fetch }).authors(4)).toEqual(["ana", "ben"]);
|
|
219
|
+
expect(calls.map((c) => c.url)).toContain("https://codeberg.org/api/v1/repos/acme/infra/pulls/4/commits?limit=50&page=1");
|
|
220
|
+
});
|
|
221
|
+
|
|
198
222
|
test("a dismissed approval no longer counts, and a comment review decides nothing", async () => {
|
|
199
223
|
const { fetch } = fakeFetch({
|
|
200
224
|
"GET /api/v1/repos/acme/infra/pulls/4/reviews": [
|
|
@@ -240,6 +264,24 @@ describe("gitlab", () => {
|
|
|
240
264
|
expect(calls[0].body).toEqual({ state: "failed", name: "chant/apply", description: "refused" });
|
|
241
265
|
});
|
|
242
266
|
|
|
267
|
+
test("the authors of a merge request: its author, and each commit email's account by the users search (#4000)", async () => {
|
|
268
|
+
const { fetch, calls } = fakeFetch({
|
|
269
|
+
"GET /api/v4/projects/42/merge_requests/5": { iid: 5, author: { username: "ana" } },
|
|
270
|
+
"GET /api/v4/projects/42/merge_requests/5/commits": [
|
|
271
|
+
{ author_email: "ana@example.com", committer_email: "Ben@Example.com" },
|
|
272
|
+
{ author_email: "ghost@example.com", committer_email: "ana@example.com" },
|
|
273
|
+
],
|
|
274
|
+
"GET /api/v4/users": (c: Call) =>
|
|
275
|
+
({ "ana@example.com": [{ username: "ana" }], "ben@example.com": [{ username: "ben" }] })[new URL(c.url).searchParams.get("search")!] ?? [],
|
|
276
|
+
});
|
|
277
|
+
expect(await gitlabForge({ ...opts, fetch }).authors(5)).toEqual(["ana", "ben"]);
|
|
278
|
+
expect(calls.filter((c) => new URL(c.url).pathname === "/api/v4/users").map((c) => new URL(c.url).searchParams.get("search"))).toEqual([
|
|
279
|
+
"ana@example.com",
|
|
280
|
+
"ben@example.com",
|
|
281
|
+
"ghost@example.com",
|
|
282
|
+
]);
|
|
283
|
+
});
|
|
284
|
+
|
|
243
285
|
test("approvers come from the approvals endpoint, merged requests from the commit", async () => {
|
|
244
286
|
const { fetch } = fakeFetch({
|
|
245
287
|
"GET /api/v4/projects/42/merge_requests/5/approvals": { approved_by: [{ user: { username: "ana" } }, { user: { username: "ben" } }] },
|
|
@@ -912,3 +954,32 @@ describe("pull requests over REST (#3798)", () => {
|
|
|
912
954
|
await expect(forgejoForge({ ...fjOpts, fetch }).openPullRequest({ head: "h", title: "t", body: "b" })).rejects.toThrow(/no default branch.*pass base/);
|
|
913
955
|
});
|
|
914
956
|
});
|
|
957
|
+
|
|
958
|
+
describe("setCommitStatus, a runner job's status (#4097)", () => {
|
|
959
|
+
const long = "x".repeat(300);
|
|
960
|
+
|
|
961
|
+
test("GitHub posts state, context, a description cut to 140 and target_url", async () => {
|
|
962
|
+
const { fetch, calls } = fakeFetch({ [`POST /repos/acme/infra/statuses/${SHA}`]: {} });
|
|
963
|
+
const forge = githubForge({ apiBase: "https://api.github.com", repo: "acme/infra", token: "t0k", fetch });
|
|
964
|
+
await forge.setCommitStatus!(SHA, { context: "chant/pr-plan", state: "failure", description: long, targetUrl: "https://host/reports/7/abc/" });
|
|
965
|
+
expect(calls).toHaveLength(1);
|
|
966
|
+
expect(calls[0]).toMatchObject({ method: "POST", url: `https://api.github.com/repos/acme/infra/statuses/${SHA}` });
|
|
967
|
+
expect(calls[0].headers.Authorization).toBe("Bearer t0k");
|
|
968
|
+
expect(calls[0].body).toEqual({ state: "failure", context: "chant/pr-plan", description: "x".repeat(140), target_url: "https://host/reports/7/abc/" });
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
test("Forgejo posts to /repos/{owner}/{repo}/statuses/{sha} under /api/v1, and omits target_url when there is none", async () => {
|
|
972
|
+
const { fetch, calls } = fakeFetch({ [`POST /api/v1/repos/acme/infra/statuses/${SHA}`]: {} });
|
|
973
|
+
const forge = forgejoForge({ apiBase: "https://codeberg.org/api/v1", repo: "acme/infra", token: "t0k", host: "codeberg.org", fetch });
|
|
974
|
+
await forge.setCommitStatus!(SHA, { context: "chant/pr-apply", state: "pending", description: "pr-apply running" });
|
|
975
|
+
expect(calls[0]).toMatchObject({ method: "POST", url: `https://codeberg.org/api/v1/repos/acme/infra/statuses/${SHA}` });
|
|
976
|
+
expect(calls[0].headers.Authorization).toBe("token t0k");
|
|
977
|
+
expect(calls[0].body).toEqual({ state: "pending", context: "chant/pr-apply", description: "pr-apply running" });
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
test("a refusal throws, and GitLab's client has none", async () => {
|
|
981
|
+
const { fetch } = fakeFetch({ [`POST /repos/acme/infra/statuses/${SHA}`]: 403 });
|
|
982
|
+
await expect(githubForge({ apiBase: "https://api.github.com", repo: "acme/infra", token: "t", fetch }).setCommitStatus!(SHA, { context: "c", state: "success", description: "d" })).rejects.toBeInstanceOf(ForgeApiError);
|
|
983
|
+
expect(gitlabForge({ apiBase: "https://gl/api/v4", project: "42", token: "t", fetch }).setCommitStatus).toBeUndefined();
|
|
984
|
+
});
|
|
985
|
+
});
|
package/src/pr-forge.ts
CHANGED
|
@@ -61,6 +61,16 @@ export interface ForgeStatus {
|
|
|
61
61
|
pipelineId?: number;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/** A runner job's commit status (#4097): the states GitHub and Forgejo both keep. */
|
|
65
|
+
export interface CommitStatus {
|
|
66
|
+
context: string;
|
|
67
|
+
state: "pending" | "success" | "failure" | "error";
|
|
68
|
+
/** Cut to 140 characters. */
|
|
69
|
+
description: string;
|
|
70
|
+
/** Where the status links to. Omitted from the request when absent. */
|
|
71
|
+
targetUrl?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
64
74
|
/** A commit status's state, as a caller names it. */
|
|
65
75
|
export type ForgeStatusState = "pending" | "running" | "success" | "failed" | "failure" | "canceled" | "error";
|
|
66
76
|
|
|
@@ -309,6 +319,14 @@ export interface PrForge {
|
|
|
309
319
|
*/
|
|
310
320
|
addComment(pr: number, body: string): Promise<AddedComment>;
|
|
311
321
|
setStatus(sha: string, status: ForgeStatus): Promise<void>;
|
|
322
|
+
/**
|
|
323
|
+
* GitHub and Forgejo (#4097): set a commit status from a runner's job, one
|
|
324
|
+
* of `pending`, `success`, `failure` or `error`. The request is the one
|
|
325
|
+
* {@link setStatus} sends (`POST /repos/{owner}/{repo}/statuses/{sha}`), with
|
|
326
|
+
* the description cut to 140 characters and `target_url` only when given.
|
|
327
|
+
* `chant ci run` posts a job's progress through it.
|
|
328
|
+
*/
|
|
329
|
+
setCommitStatus?(sha: string, status: CommitStatus): Promise<void>;
|
|
312
330
|
/**
|
|
313
331
|
* Keep one OPEN tracking issue for `marker` in line with `finding`: open
|
|
314
332
|
* it, update its title and body, or, when `finding` is `null` and
|
|
@@ -331,6 +349,16 @@ export interface PrForge {
|
|
|
331
349
|
upsertIssue(marker: string, title: string, body: string): Promise<string | undefined>;
|
|
332
350
|
/** Logins whose standing review approves `pr`. */
|
|
333
351
|
approvers(pr: number): Promise<string[]>;
|
|
352
|
+
/**
|
|
353
|
+
* Logins of the people who wrote `pr` (#4000): its author, and the author
|
|
354
|
+
* and committer of each of its commits. GitHub and Forgejo name a commit's
|
|
355
|
+
* people by the account its email belongs to; a commit whose email is on no
|
|
356
|
+
* account adds nobody. GitLab names them by email only, so each email is
|
|
357
|
+
* looked up among the users (`GET /users?search=`), which matches an
|
|
358
|
+
* account's public email; an email no account shows publicly adds nobody.
|
|
359
|
+
* Sorted, without repeats.
|
|
360
|
+
*/
|
|
361
|
+
authors(pr: number): Promise<string[]>;
|
|
334
362
|
/** The merged pull request that produced `sha`, or null. */
|
|
335
363
|
pullRequestFor(sha: string): Promise<number | null>;
|
|
336
364
|
/**
|
|
@@ -614,6 +642,14 @@ function githubShaped(kind: "github" | "forgejo", opts: GithubForgeOptions): Git
|
|
|
614
642
|
await api.call("PATCH", `${repo}/issues/${n}`, { state: "closed" });
|
|
615
643
|
},
|
|
616
644
|
};
|
|
645
|
+
const postStatus = async (sha: string, state: ForgeStatusState, context: string, description: string, url?: string): Promise<void> => {
|
|
646
|
+
await api.call("POST", `${repo}/statuses/${sha}`, {
|
|
647
|
+
state: statusStateFor(kind, state),
|
|
648
|
+
context,
|
|
649
|
+
description: description.slice(0, GITHUB_DESCRIPTION),
|
|
650
|
+
...(url ? { target_url: url } : {}),
|
|
651
|
+
});
|
|
652
|
+
};
|
|
617
653
|
return {
|
|
618
654
|
kind,
|
|
619
655
|
host,
|
|
@@ -621,12 +657,10 @@ function githubShaped(kind: "github" | "forgejo", opts: GithubForgeOptions): Git
|
|
|
621
657
|
principalOf: (login) => forgePrincipalOf(kind, host, login),
|
|
622
658
|
...threadCalls(t),
|
|
623
659
|
async setStatus(sha, status) {
|
|
624
|
-
await
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
...(status.url ? { target_url: status.url } : {}),
|
|
629
|
-
});
|
|
660
|
+
await postStatus(sha, status.state, status.context, status.description, status.url);
|
|
661
|
+
},
|
|
662
|
+
async setCommitStatus(sha, status) {
|
|
663
|
+
await postStatus(sha, status.state, status.context, status.description, status.targetUrl);
|
|
630
664
|
},
|
|
631
665
|
async approvers(pr) {
|
|
632
666
|
// A reviewer's newest deciding review is their standing one. A comment
|
|
@@ -643,6 +677,20 @@ function githubShaped(kind: "github" | "forgejo", opts: GithubForgeOptions): Git
|
|
|
643
677
|
}
|
|
644
678
|
return [...standing].filter(([, state]) => state === "APPROVED").map(([login]) => login).sort();
|
|
645
679
|
},
|
|
680
|
+
async authors(pr) {
|
|
681
|
+
const logins = new Set<string>();
|
|
682
|
+
const add = (v: unknown) => {
|
|
683
|
+
if (isObject(v) && typeof v.login === "string" && v.login !== "") logins.add(v.login);
|
|
684
|
+
};
|
|
685
|
+
const opened = await api.call("GET", `${repo}/pulls/${pr}`);
|
|
686
|
+
if (isObject(opened)) add(opened.user);
|
|
687
|
+
for (const c of await api.list(`${repo}/pulls/${pr}/commits`, pageParams, pageSize)) {
|
|
688
|
+
if (!isObject(c)) continue;
|
|
689
|
+
add(c.author);
|
|
690
|
+
add(c.committer);
|
|
691
|
+
}
|
|
692
|
+
return [...logins].sort();
|
|
693
|
+
},
|
|
646
694
|
async pullRequestFor(sha) {
|
|
647
695
|
if (kind === "forgejo") {
|
|
648
696
|
try {
|
|
@@ -898,6 +946,22 @@ export function gitlabForge(opts: GitlabForgeOptions): PrForge {
|
|
|
898
946
|
.filter((l): l is string => l !== undefined);
|
|
899
947
|
return [...new Set(logins)].sort();
|
|
900
948
|
},
|
|
949
|
+
async authors(mr) {
|
|
950
|
+
const logins = new Set<string>();
|
|
951
|
+
const merge = await api.call("GET", `${notes}/${mr}`);
|
|
952
|
+
const author = isObject(merge) ? authorOf(merge) : undefined;
|
|
953
|
+
if (author) logins.add(author);
|
|
954
|
+
const emails = new Set<string>();
|
|
955
|
+
for (const c of await api.list(`${notes}/${mr}/commits`, pageParams, 100)) {
|
|
956
|
+
if (!isObject(c)) continue;
|
|
957
|
+
for (const email of [str(c.author_email), str(c.committer_email)]) if (email) emails.add(email.toLowerCase());
|
|
958
|
+
}
|
|
959
|
+
for (const email of [...emails].sort()) {
|
|
960
|
+
const users = await api.call("GET", `/users?search=${encodeURIComponent(email)}`);
|
|
961
|
+
for (const u of Array.isArray(users) ? users : []) if (isObject(u) && typeof u.username === "string") logins.add(u.username);
|
|
962
|
+
}
|
|
963
|
+
return [...logins].sort();
|
|
964
|
+
},
|
|
901
965
|
async pullRequestFor(sha) {
|
|
902
966
|
const mrs = await api.call("GET", `${project}/repository/commits/${sha}/merge_requests`);
|
|
903
967
|
if (!Array.isArray(mrs)) return null;
|
|
@@ -41,6 +41,7 @@ describe("the pull-request loop bundle", () => {
|
|
|
41
41
|
"packages/core/src/pr-forge.ts",
|
|
42
42
|
"packages/core/src/pr-loop.ts",
|
|
43
43
|
]);
|
|
44
|
-
|
|
44
|
+
// About 65 KB today, after the pull request authors (#4000).
|
|
45
|
+
expect(result.outputFiles.reduce((n, f) => n + f.contents.byteLength, 0)).toBeLessThan(72 * 1024);
|
|
45
46
|
});
|
|
46
47
|
});
|
package/src/pr-loop.test.ts
CHANGED
|
@@ -14,7 +14,10 @@ import {
|
|
|
14
14
|
describePlanChanged,
|
|
15
15
|
memberCounts,
|
|
16
16
|
PR_REPORT_DEPLOYED_OUTPUTS_FLOOR,
|
|
17
|
+
PR_REPORT_PLANNED_OUTPUTS_FLOOR,
|
|
18
|
+
prRoundGate,
|
|
17
19
|
PR_REPORT_SCHEMA_ID,
|
|
20
|
+
PR_REPORT_WAVES_FLOOR,
|
|
18
21
|
prApproveCommand,
|
|
19
22
|
prNoteMarker,
|
|
20
23
|
PR_GATE_OP,
|
|
@@ -113,6 +116,34 @@ const resumedReport: PrReport = {
|
|
|
113
116
|
/** A plan whose members read `net` and `a` from the deployed estate (#4104). */
|
|
114
117
|
const deployedReport: PrReport = { ...planReport, deployedOutputs: { a: ["id"], net: ["cidr"], dns: [] } };
|
|
115
118
|
|
|
119
|
+
/** A plan where `app` reads `a.id`, which `a`'s plan knows only after apply, and `a.arn`, which it knows (#3999). */
|
|
120
|
+
const unknownReport: PrReport = {
|
|
121
|
+
...planReport,
|
|
122
|
+
deployedOutputs: { net: ["cidr"] },
|
|
123
|
+
plannedOutputs: { a: ["arn"] },
|
|
124
|
+
members: [planReport.members[0]!, { ...planReport.members[1]!, unknownInputs: ["a.id"], buildInputs: ["img.publish.digest"] }],
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const roundDoc = composeChangeSet([part("app", 3, [{ address: "terraform_data.app", action: "update" }])]);
|
|
128
|
+
/** The apply of {@link unknownReport}: `a` applied, and `app`, planned again, waits on round 2's gate (#3999). */
|
|
129
|
+
const waitingReport: PrReport = {
|
|
130
|
+
...unknownReport,
|
|
131
|
+
stage: "apply",
|
|
132
|
+
status: "waiting",
|
|
133
|
+
approval: { status: "approved", approvedBy: ["github:alice"] },
|
|
134
|
+
rounds: [{ round: 2, gate: prRoundGate("pr-apply", 2), digest: roundDoc.digest, components: ["app"], approval: { status: "pending" }, status: "waiting", changeSet: roundDoc }],
|
|
135
|
+
members: [
|
|
136
|
+
{ ...planReport.members[0]!, status: "applied" },
|
|
137
|
+
{
|
|
138
|
+
...unknownReport.members[1]!,
|
|
139
|
+
status: "waiting",
|
|
140
|
+
round: 2,
|
|
141
|
+
planDigest: `jcs1-sha256:${hex(3)}`,
|
|
142
|
+
held: "its plan read a.id before that output applied as another value",
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
|
|
116
147
|
describe("the pr-report schema", () => {
|
|
117
148
|
test("is a valid schema and names this module's id", () => {
|
|
118
149
|
expect(validSchema(schema)).toBe(true);
|
|
@@ -120,7 +151,7 @@ describe("the pr-report schema", () => {
|
|
|
120
151
|
});
|
|
121
152
|
|
|
122
153
|
test("every stage's report validates, and its change set validates against the change-set schema", () => {
|
|
123
|
-
for (const report of [planReport, refusedReport, appliedReport, resumedReport, deployedReport]) {
|
|
154
|
+
for (const report of [planReport, refusedReport, appliedReport, resumedReport, deployedReport, unknownReport, waitingReport]) {
|
|
124
155
|
expectValid(report);
|
|
125
156
|
expectValidChangeSet(report.changeSet);
|
|
126
157
|
expect(report.changeSet.digest).toBe(report.digest);
|
|
@@ -170,6 +201,27 @@ describe("the note", () => {
|
|
|
170
201
|
expect(schema.required).not.toContain("deployedOutputs");
|
|
171
202
|
});
|
|
172
203
|
|
|
204
|
+
test("a plan names the outputs read from upstream plans and the ones known only after apply (#3999)", () => {
|
|
205
|
+
const note = renderPrNote(unknownReport);
|
|
206
|
+
expect(note).toContain("Planned against the outputs this change's plans say its upstreams will have: `a` (`arn`).");
|
|
207
|
+
expect(note).toContain("Known only after apply: `app` reads `a.id`. This approval does not cover what those members do with them.");
|
|
208
|
+
expect(renderPrNote(planReport)).not.toContain("Known only after apply");
|
|
209
|
+
expect(note).toContain("From this pull request's build: `app` reads `img.publish.digest`. The image digest comes from this pull request's build");
|
|
210
|
+
expect(renderPrNote(planReport)).not.toContain("From this pull request's build");
|
|
211
|
+
expect(PR_REPORT_PLANNED_OUTPUTS_FLOOR).toBe("0.124.32");
|
|
212
|
+
expect(schema.required).not.toContain("plannedOutputs");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("a waiting apply names the round's digest and the command that approves it (#3999)", () => {
|
|
216
|
+
const note = renderPrNote(waitingReport, { approver: "github:<your-login>" });
|
|
217
|
+
expect(note).toContain("Applied 1 of 2 members; 1 wait on an approval of the plan made after their upstream applied.");
|
|
218
|
+
expect(note).toContain(`chant approve pr-12 pr-apply-round-2 --plan ${roundDoc.digest} --approver github:<your-login> --sign`);
|
|
219
|
+
expect(note).toContain("| `app` | app | waiting on its new plan |");
|
|
220
|
+
expect(prStatusState(waitingReport)).toBe("pending");
|
|
221
|
+
expect(prStatusDescription(waitingReport)).toBe("waiting: app planned again after an upstream applied, not approved yet");
|
|
222
|
+
expect(prRoundGate("pr-apply", 1)).toBe("pr-apply");
|
|
223
|
+
});
|
|
224
|
+
|
|
173
225
|
test("a small limit leaves the summary out rather than cutting the table", () => {
|
|
174
226
|
const note = renderPrNote(planReport, { limit: 800 });
|
|
175
227
|
expect(note).toContain("| `app` |");
|
|
@@ -181,6 +233,7 @@ describe("the status", () => {
|
|
|
181
233
|
test("describes each stage in at most 140 characters", () => {
|
|
182
234
|
expect(prStatusDescription(planReport)).toMatch(/^2 members: 1 to change, 0 to destroy or replace; plan [0-9a-f]{12}$/);
|
|
183
235
|
expect(prStatusDescription(refusedReport)).toBe("refused: the plan changed after review");
|
|
236
|
+
expect(prStatusDescription({ ...refusedReport, refusal: "author-approved" })).toBe("refused: the approver wrote the change");
|
|
184
237
|
expect(prStatusDescription(appliedReport)).toMatch(/^applied 2 members/);
|
|
185
238
|
for (const r of [planReport, refusedReport, appliedReport]) expect(prStatusDescription(r).length).toBeLessThanOrEqual(140);
|
|
186
239
|
});
|
|
@@ -221,3 +274,66 @@ describe("a workspace member's loop (#3465)", () => {
|
|
|
221
274
|
expect(note).toContain("chant approve pr-12-network pr-apply");
|
|
222
275
|
});
|
|
223
276
|
});
|
|
277
|
+
|
|
278
|
+
describe("a gate per wave on merge (#4003)", () => {
|
|
279
|
+
const waveDigest = `jcs1-sha256:${hex(7)}`;
|
|
280
|
+
const wave1 = { wave: 1, components: ["a"], status: "applied" as const, approval: "pull-request" as const, gate: "pr-apply", digest: doc.digest, approvedBy: ["github:alice"] };
|
|
281
|
+
const waveApplied: PrReport = {
|
|
282
|
+
...appliedReport,
|
|
283
|
+
status: "wave-applied",
|
|
284
|
+
waveGate: "release",
|
|
285
|
+
waves: [wave1],
|
|
286
|
+
members: [{ ...planReport.members[0], status: "applied" }, planReport.members[1]],
|
|
287
|
+
};
|
|
288
|
+
const waiting: PrReport = {
|
|
289
|
+
...waveApplied,
|
|
290
|
+
status: "refused",
|
|
291
|
+
refusal: "wave-not-approved",
|
|
292
|
+
message: "Wave 2 was planned after the waves before it applied, and the pull request's approved plan does not cover it.",
|
|
293
|
+
waves: [
|
|
294
|
+
wave1,
|
|
295
|
+
{ wave: 2, components: ["app"], status: "waiting", approval: "wave", gate: "release-wave-2", digest: waveDigest, moved: ["app now writes app.main.cidr to a different value than the approved plan"] },
|
|
296
|
+
],
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
test("the reports validate, waveGate and waves are optional, and both came with the same chant", () => {
|
|
300
|
+
for (const r of [waveApplied, waiting]) expectValid(r);
|
|
301
|
+
expect(schema.required).not.toContain("waves");
|
|
302
|
+
expect(schema.required).not.toContain("waveGate");
|
|
303
|
+
expect(PR_REPORT_WAVES_FLOOR).toBe("0.124.32");
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("the note has a row per wave saying whose approval it applied under", () => {
|
|
307
|
+
const note = renderPrNote(waveApplied);
|
|
308
|
+
expect(note).toContain("Applied wave 1 of 2. The next wave runs in its own job.");
|
|
309
|
+
expect(note).toContain("| 1 | `a` | applied | the pull request's `pr-apply` by `github:alice` |");
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("a wave that waits names why its plan moved and the approve command for its own gate", () => {
|
|
313
|
+
const note = renderPrNote(waiting, { approver: "github:<your-login>" });
|
|
314
|
+
expect(note).toContain("Wave 2 waits at its gate `release-wave-2`; nothing in it applied.");
|
|
315
|
+
expect(note).toContain("| 2 | `app` | waiting | its own `release-wave-2`, not approved yet |");
|
|
316
|
+
expect(note).toContain("- app now writes app.main.cidr to a different value than the approved plan");
|
|
317
|
+
expect(note).toContain(`chant approve pr-12 release-wave-2 --plan ${waveDigest} --approver github:<your-login> --sign`);
|
|
318
|
+
golden("pr-apply-wave-waiting.note.golden.md", note);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("with --wave-gate the plan's note says later waves are planned again and may stop at their own gate; without it, nothing", () => {
|
|
322
|
+
const sentence = "On merge each wave after the first is planned again once the waves before it apply, and a wave whose plan moved stops at its own gate, `release-wave-<k>`, for approval.";
|
|
323
|
+
const planned = renderPrNote({ ...planReport, waveGate: "release" });
|
|
324
|
+
expect(planned).toContain(sentence);
|
|
325
|
+
expect(renderPrNote({ ...planReport, waveGate: "release", approval: { status: "approved", approvedBy: ["github:alice"] } })).toContain(sentence);
|
|
326
|
+
expect(renderPrNote(planReport)).not.toContain("planned again");
|
|
327
|
+
expect(renderPrNote(waveApplied)).not.toContain("planned again");
|
|
328
|
+
expectValid({ ...planReport, waveGate: "release" });
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("the status: a wave applied succeeds, a wave waiting on its approval is pending, a moved approval fails", () => {
|
|
332
|
+
expect(prStatusDescription(waveApplied)).toBe("applied wave 1 of 2 under the pull request's approval");
|
|
333
|
+
expect(prStatusState(waveApplied)).toBe("success");
|
|
334
|
+
expect(prStatusDescription(waiting)).toBe("wave 2 waits at release-wave-2: its plan moved, approve it");
|
|
335
|
+
expect(prStatusState(waiting)).toBe("pending");
|
|
336
|
+
expect(prStatusState({ ...waiting, refusal: "wave-plan-changed" })).toBe("failure");
|
|
337
|
+
expect(prStatusDescription({ ...waiting, waves: [wave1], refusal: "earlier-wave" })).toBe("refused: an earlier wave has not applied");
|
|
338
|
+
});
|
|
339
|
+
});
|