@intentius/chant-lexicon-github 0.124.55 → 0.124.56
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/components/generate-op-pipeline.d.ts.map +1 -1
- package/dist/components/generate-op-pipeline.js +71 -6
- package/dist/components/generate-op-waves-pipeline.d.ts +14 -1
- package/dist/components/generate-op-waves-pipeline.d.ts.map +1 -1
- package/dist/components/generate-op-waves-pipeline.js +74 -13
- package/dist/components/generate-pipeline.d.ts.map +1 -1
- package/dist/components/generate-pipeline.js +30 -4
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/package.json +2 -2
- package/src/components/__golden__/op-waves-jobs.yml +20 -0
- package/src/components/generate-op-pipeline.cloud.test.ts +96 -0
- package/src/components/generate-op-pipeline.ts +85 -11
- package/src/components/generate-op-waves-pipeline.approval.test.ts +46 -0
- package/src/components/generate-op-waves-pipeline.guard.test.ts +57 -0
- package/src/components/generate-op-waves-pipeline.identity.test.ts +8 -8
- package/src/components/generate-op-waves-pipeline.ts +76 -11
- package/src/components/generate-pipeline.ts +33 -3
- package/src/components/generate-pr-pipeline.comment-targets.test.ts +24 -0
- package/src/components/generate-pr-pipeline.respond.test.ts +47 -0
- package/src/components/generate-pr-pipeline.runners.test.ts +61 -0
- package/src/components/generate-pr-pipeline.stale-notes.test.ts +42 -0
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
import { resolveComponentGraph, type DriverComponent } from "@intentius/chant/components/driver";
|
|
60
60
|
import { hasPublishStep, promoteArchivePaths } from "@intentius/chant/components/promote";
|
|
61
61
|
import { GATED_WAVE_RECORD, gatedWaveJobs } from "@intentius/chant/components/gated-wave-pipeline";
|
|
62
|
-
import { CI_IMAGE, ciJobName, dirnameOf, fillCommand, GITHUB_JOB_TOKEN, GITHUB_RUNNER } from "@intentius/chant/ci-jobs";
|
|
62
|
+
import { CI_IMAGE, ciJobName, dirnameOf, fillCommand, GITHUB_JOB_TOKEN, GITHUB_RUNNER, githubRunsOn } from "@intentius/chant/ci-jobs";
|
|
63
63
|
import { CHECKOUT_TOKEN_ENV, checkoutScript, dropForgeTokens, handoverOutputs, handoverTrap, takeOverScript } from "@intentius/chant/ci-handover";
|
|
64
64
|
import { OIDC_REQUEST_VARS, TRADE_SPEC_ENV, tradeLine, type CloudTradeSpec } from "@intentius/chant/ci-identity";
|
|
65
65
|
import {
|
|
@@ -87,6 +87,10 @@ import {
|
|
|
87
87
|
prLoopWaveJobs,
|
|
88
88
|
prLoopWithWaves,
|
|
89
89
|
prEnvJobs,
|
|
90
|
+
prLoopJobStage,
|
|
91
|
+
prStaleJob,
|
|
92
|
+
PR_STALE_JOB,
|
|
93
|
+
prLoopRunner,
|
|
90
94
|
PR_ENV_REPORT_DIR,
|
|
91
95
|
PR_ENV_SWEEP_CRON,
|
|
92
96
|
PR_ENV_POST_JOB,
|
|
@@ -415,7 +419,25 @@ function prLoopGithubDoc(
|
|
|
415
419
|
},
|
|
416
420
|
]);
|
|
417
421
|
}
|
|
418
|
-
const post = prLoopPostJob(forgeName, env, member?.name, loop.words ? resolveProductWords(loop.words) : undefined);
|
|
422
|
+
const post = prLoopPostJob(forgeName, env, member?.name, loop.words ? resolveProductWords(loop.words) : undefined, { ...(loop.staleNotes ? { staleNotes: true } : {}), ...(loop.respond ? { respond: loop.respond } : {}), ...(loop.decide ? { decide: loop.decide } : {}) });
|
|
423
|
+
// #4628: on a push to the target branch, the open pull requests' notes posted against another commit are marked stale.
|
|
424
|
+
const stale = prStaleJob(forgeName, env, loop, member?.name);
|
|
425
|
+
const staleJobs: Record<string, unknown> = stale
|
|
426
|
+
? {
|
|
427
|
+
[PR_STALE_JOB]: {
|
|
428
|
+
if: "github.event_name == 'push' && startsWith(github.ref, 'refs/heads/')",
|
|
429
|
+
"runs-on": GITHUB_RUNNER,
|
|
430
|
+
container: image,
|
|
431
|
+
permissions: { contents: "read", "pull-requests": "write", statuses: "write" },
|
|
432
|
+
steps: [
|
|
433
|
+
// The pushed commit: the target branch's, so none of a pull request's code is here.
|
|
434
|
+
{ name: "Check out the target branch's commit", uses: CHECKOUT },
|
|
435
|
+
...runSteps(beforeScript),
|
|
436
|
+
{ name: "Mark the open pull requests' notes stale", ...(rooted ? {} : { "working-directory": "." }), env: stale.env, run: stale.command },
|
|
437
|
+
],
|
|
438
|
+
},
|
|
439
|
+
}
|
|
440
|
+
: {};
|
|
419
441
|
// #4619: with a control repo named, the note job reads its chant/lifecycle, read-only, so pr-post marks the cross-stack edges.
|
|
420
442
|
const controlRead = loop.control ? controlNoteCheckout(forgeName, loop.control.target, loop.words ?? {}) : undefined;
|
|
421
443
|
const noteSteps: Array<Record<string, unknown>> = [
|
|
@@ -563,6 +585,7 @@ function prLoopGithubDoc(
|
|
|
563
585
|
...lockJobs,
|
|
564
586
|
...commentLockJobs,
|
|
565
587
|
...previewJobs,
|
|
588
|
+
...staleJobs,
|
|
566
589
|
...Object.fromEntries(applyJobs),
|
|
567
590
|
...Object.fromEntries(controlJobs),
|
|
568
591
|
...(prEnv ? prEnv.jobs : {}),
|
|
@@ -580,6 +603,12 @@ function prLoopGithubDoc(
|
|
|
580
603
|
}
|
|
581
604
|
// #4301: a /chant apply comment starts the apply before merge. #4588: and a lock or unlock comment its job.
|
|
582
605
|
if (loop.applyBeforeMerge || commentLocks.length > 0 || preview) on.issue_comment = { types: ["created"] };
|
|
606
|
+
// #4625: each job on its stage's runner, the plan side's, the apply side's, or the default.
|
|
607
|
+
if (loop.runners) {
|
|
608
|
+
for (const [name, job] of Object.entries(jobsDoc) as Array<[string, Record<string, unknown>]>) {
|
|
609
|
+
job["runs-on"] = githubRunsOn(prLoopRunner(loop, prLoopJobStage(name)));
|
|
610
|
+
}
|
|
611
|
+
}
|
|
583
612
|
return {
|
|
584
613
|
name: member ? `chant-pr.${member.name}.${env}` : `chant-pr-${env}`,
|
|
585
614
|
environment: env,
|
|
@@ -587,10 +616,11 @@ function prLoopGithubDoc(
|
|
|
587
616
|
...(rooted ? {} : { defaults: { run: { "working-directory": member!.dir } } }),
|
|
588
617
|
env: { ...options.variables, CHANT_ENV: env },
|
|
589
618
|
jobsDoc,
|
|
590
|
-
stages: ["plan", PR_PLAN_NOTE_JOB, ...(locks ? [PR_LOCK_JOB, PR_UNLOCK_JOB] : []), ...commentLocks.map((j) => j.jobName), ...(preview ? [preview.jobName, preview.noteJobName] : []), ...applyNames, ...controlJobs.map(([name]) => name), ...(prEnv ? ["env-up", PR_ENV_POST_JOB, "env-down", "env-sweep"] : [])],
|
|
619
|
+
stages: ["plan", PR_PLAN_NOTE_JOB, ...(locks ? [PR_LOCK_JOB, PR_UNLOCK_JOB] : []), ...commentLocks.map((j) => j.jobName), ...(preview ? [preview.jobName, preview.noteJobName] : []), ...(stale ? [PR_STALE_JOB] : []), ...applyNames, ...controlJobs.map(([name]) => name), ...(prEnv ? ["env-up", PR_ENV_POST_JOB, "env-down", "env-sweep"] : [])],
|
|
591
620
|
jobs: [
|
|
592
621
|
{ jobName: "plan", component: "pull request plan", stage: "plan", needs: [] },
|
|
593
622
|
{ jobName: PR_PLAN_NOTE_JOB, component: "pull request plan note", stage: PR_PLAN_NOTE_JOB, needs: ["plan"] },
|
|
623
|
+
...(stale ? [{ jobName: PR_STALE_JOB, component: "pull request stale notes", stage: PR_STALE_JOB, needs: [] }] : []),
|
|
594
624
|
...(locks
|
|
595
625
|
? [
|
|
596
626
|
{ jobName: PR_LOCK_JOB, component: "pull request lock", stage: PR_LOCK_JOB, needs: ["plan"] },
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preview comment's --target on GitHub (#4626): the preview step runs
|
|
3
|
+
* `pr-plan --comment-targets` with the comment body in CHANT_PREVIEW_COMMENT,
|
|
4
|
+
* and the body never appears in the shell line. Written for the
|
|
5
|
+
* ci-products post-merge pass.
|
|
6
|
+
*/
|
|
7
|
+
import { expect, test } from "vitest";
|
|
8
|
+
import { parseYAML } from "@intentius/chant/yaml";
|
|
9
|
+
import type { DriverComponent } from "@intentius/chant/components/driver";
|
|
10
|
+
import { generateGithubPipeline } from "./generate-pipeline";
|
|
11
|
+
|
|
12
|
+
const ESTATE: DriverComponent[] = [{ name: "net", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "terraform-apply", root: "net" }] }] }];
|
|
13
|
+
const WORDS = { product: "plywood", stages: { plan: "preview", apply: "up" }, commands: { preview: "plan" as const }, bin: "npx --no-install plywood" };
|
|
14
|
+
|
|
15
|
+
test("the preview step reads the comment from a variable (#4626)", () => {
|
|
16
|
+
const doc = parseYAML(generateGithubPipeline(ESTATE, { env: "prod", prLoop: { previewComment: true, words: WORDS } }).yaml) as { jobs: Record<string, { steps: Array<{ name?: string; run?: string; env?: Record<string, string> }> }> };
|
|
17
|
+
const step = doc.jobs["comment-preview"]!.steps.find((s) => s.name === "Preview the members this pull request reaches")!;
|
|
18
|
+
expect(step.run).toContain("npx --no-install plywood ci run plan");
|
|
19
|
+
expect(step.run).toContain("--comment-targets");
|
|
20
|
+
expect(step.env!.CHANT_PREVIEW_COMMENT).toBe("${{ github.event.comment.body }}");
|
|
21
|
+
expect(step.run).not.toContain("comment.body");
|
|
22
|
+
const plan = doc.jobs.plan!.steps.find((s) => s.name === "Plan the members this pull request reaches")!;
|
|
23
|
+
expect(plan.run).not.toContain("--comment-targets");
|
|
24
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pull request loop's responses, description check and merge on GitHub
|
|
3
|
+
* (#4629, #4636), as `chant build --pr-loop --respond --decide-backend
|
|
4
|
+
* --decide-model --merge auto` sets them: the note job asks the decide
|
|
5
|
+
* service, and the apply job carries CHANT_RESPOND and the merge.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, expect, test } from "vitest";
|
|
8
|
+
import { parseYAML } from "@intentius/chant/yaml";
|
|
9
|
+
import type { DriverComponent } from "@intentius/chant/components/driver";
|
|
10
|
+
import { generateGithubPipeline } from "./generate-pipeline";
|
|
11
|
+
|
|
12
|
+
const ESTATE: DriverComponent[] = [{ name: "net", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "terraform-apply", root: "net" }] }] }];
|
|
13
|
+
|
|
14
|
+
type Step = { name?: string; run?: string; env?: Record<string, string> };
|
|
15
|
+
type Doc = { jobs: Record<string, { steps: Step[] }> };
|
|
16
|
+
const gen = (prLoop: Record<string, unknown>) => parseYAML(generateGithubPipeline(ESTATE, { env: "prod", prLoop }).yaml) as Doc;
|
|
17
|
+
|
|
18
|
+
describe("responses and the merge on GitHub (#4629, #4636)", () => {
|
|
19
|
+
const doc = gen({
|
|
20
|
+
applyBeforeMerge: true,
|
|
21
|
+
applyMerge: { tokenEnv: "CHANT_MERGE_TOKEN", method: "squash" },
|
|
22
|
+
respond: { "wave-refused": "off", description: "check" },
|
|
23
|
+
decide: { backend: "review", model: "m-1" },
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("the note job carries the responses and asks the decide service", () => {
|
|
27
|
+
const post = doc.jobs["plan-note"]!.steps.at(-1)!;
|
|
28
|
+
expect(post.run).toMatch(/--decide-backend "review" --decide-model "m-1"$/);
|
|
29
|
+
expect(post.env!.CHANT_RESPOND).toBe("wave-refused=off,description=check");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("the apply job carries the responses and the merge", () => {
|
|
33
|
+
const steps = Object.values(doc.jobs).flatMap((j) => j.steps);
|
|
34
|
+
const apply = steps.find((s) => s.run?.includes("--apply-before-merge"))!;
|
|
35
|
+
expect(apply.run).toContain("--merge auto --merge-token-env CHANT_MERGE_TOKEN --merge-method squash");
|
|
36
|
+
expect(apply.env!.CHANT_RESPOND).toBe("wave-refused=off,description=check");
|
|
37
|
+
expect(apply.env!.CHANT_MERGE_TOKEN).toBe("${{ secrets.CHANT_MERGE_TOKEN }}");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("without them, neither job changes", () => {
|
|
41
|
+
const plain = gen({ applyBeforeMerge: true });
|
|
42
|
+
const post = plain.jobs["plan-note"]!.steps.at(-1)!;
|
|
43
|
+
expect(post.run).not.toContain("--decide-backend");
|
|
44
|
+
expect(post.env!.CHANT_RESPOND).toBeUndefined();
|
|
45
|
+
expect(JSON.stringify(plain)).not.toContain("--merge auto");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pull request loop's runners, variables and passed secrets on GitHub
|
|
3
|
+
* (#4625): every job on its stage's runner, and the jobs that plan or apply
|
|
4
|
+
* with the variables and the secrets mapped from the repository's. Written
|
|
5
|
+
* for the ci-products post-merge pass.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, expect, test } from "vitest";
|
|
9
|
+
import { parseYAML } from "@intentius/chant/yaml";
|
|
10
|
+
import type { DriverComponent } from "@intentius/chant/components/driver";
|
|
11
|
+
import { generateGithubPipeline } from "./generate-pipeline";
|
|
12
|
+
|
|
13
|
+
const ESTATE: DriverComponent[] = [{ name: "net", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "terraform-apply", root: "net" }] }] }];
|
|
14
|
+
const WORDS = { product: "plywood", stages: { plan: "preview", apply: "up" }, commands: { preview: "plan" as const, lock: "lock" as const, unlock: "unlock" as const } };
|
|
15
|
+
|
|
16
|
+
type Step = { name?: string; run?: string; env?: Record<string, string> };
|
|
17
|
+
type Doc = { jobs: Record<string, { "runs-on": unknown; steps: Step[] }> };
|
|
18
|
+
|
|
19
|
+
const doc = parseYAML(
|
|
20
|
+
generateGithubPipeline(ESTATE, {
|
|
21
|
+
env: "prod",
|
|
22
|
+
prLoop: {
|
|
23
|
+
words: WORDS,
|
|
24
|
+
previewComment: true,
|
|
25
|
+
locks: "plan",
|
|
26
|
+
applyOnMerge: true,
|
|
27
|
+
runners: { plan: ["self-hosted", "big"], apply: "deployer", default: "small" },
|
|
28
|
+
variables: { REGION: "eu-west-1" },
|
|
29
|
+
secrets: { DB_PASSWORD: "PROD_DB_PASSWORD" },
|
|
30
|
+
parallelism: 2,
|
|
31
|
+
},
|
|
32
|
+
}).yaml,
|
|
33
|
+
) as Doc;
|
|
34
|
+
|
|
35
|
+
describe("the GitHub pull request loop's runners and variables (#4625)", () => {
|
|
36
|
+
test("every job runs on its stage's runner", () => {
|
|
37
|
+
expect(doc.jobs.plan!["runs-on"]).toEqual(["self-hosted", "big"]);
|
|
38
|
+
expect(doc.jobs["comment-preview"]!["runs-on"]).toEqual(["self-hosted", "big"]);
|
|
39
|
+
expect(doc.jobs.apply!["runs-on"]).toBe("deployer");
|
|
40
|
+
expect(doc.jobs.record!["runs-on"]).toBe("deployer");
|
|
41
|
+
for (const name of ["plan-note", "comment-preview-note", "lock", "unlock", "comment-lock", "comment-unlock"]) expect(doc.jobs[name]!["runs-on"], name).toBe("small");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("the plan step gets the variable and the secret, and still drops the forge tokens first", () => {
|
|
45
|
+
const step = doc.jobs.plan!.steps.find((s) => s.name === "Plan the members this pull request reaches")!;
|
|
46
|
+
expect(step.env).toMatchObject({ REGION: "eu-west-1", DB_PASSWORD: "${{ secrets.PROD_DB_PASSWORD }}" });
|
|
47
|
+
expect(step.env).not.toHaveProperty("GITHUB_TOKEN");
|
|
48
|
+
expect(step.run!.split("\n")[0]).toContain("unset");
|
|
49
|
+
expect(step.run).toContain("--parallelism 2");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("the apply step gets them too", () => {
|
|
53
|
+
const step = doc.jobs.apply!.steps.find((s) => s.run?.includes("ci run apply"))!;
|
|
54
|
+
expect(step.env).toMatchObject({ REGION: "eu-west-1", DB_PASSWORD: "${{ secrets.PROD_DB_PASSWORD }}" });
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("without runners every job keeps the forge's default", () => {
|
|
58
|
+
const plain = parseYAML(generateGithubPipeline(ESTATE, { env: "prod", prLoop: {} }).yaml) as Doc;
|
|
59
|
+
expect(plain.jobs.plan!["runs-on"]).toBe("ubuntu-latest");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stale marking on GitHub (#4628): with staleNotes the note job passes the
|
|
3
|
+
* target branch's commit, and a stale-notes job on a push to the branch
|
|
4
|
+
* runs `pr-post --stale` at the pushed commit with a token that may edit the
|
|
5
|
+
* notes and set the statuses. Written for the ci-products post-merge pass.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, expect, test } from "vitest";
|
|
8
|
+
import { parseYAML } from "@intentius/chant/yaml";
|
|
9
|
+
import type { DriverComponent } from "@intentius/chant/components/driver";
|
|
10
|
+
import { generateGithubPipeline } from "./generate-pipeline";
|
|
11
|
+
|
|
12
|
+
const ESTATE: DriverComponent[] = [{ name: "net", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "terraform-apply", root: "net" }] }] }];
|
|
13
|
+
const WORDS = { product: "plywood", stages: { plan: "preview", apply: "up" }, commands: { preview: "plan" as const }, bin: "npx --no-install plywood" };
|
|
14
|
+
|
|
15
|
+
type Step = { name?: string; run?: string; env?: Record<string, string>; uses?: string };
|
|
16
|
+
type Doc = { jobs: Record<string, { if?: string; permissions?: Record<string, string>; steps: Step[] }> };
|
|
17
|
+
const gen = (prLoop: Record<string, unknown>) => parseYAML(generateGithubPipeline(ESTATE, { env: "prod", prLoop }).yaml) as Doc;
|
|
18
|
+
|
|
19
|
+
describe("stale notes on GitHub (#4628)", () => {
|
|
20
|
+
const doc = gen({ words: WORDS, staleNotes: true, previewComment: true });
|
|
21
|
+
|
|
22
|
+
test("the note job passes the target branch's commit", () => {
|
|
23
|
+
const post = doc.jobs["plan-note"]!.steps.at(-1)!;
|
|
24
|
+
expect(post.run).toContain('--tip "$TIP_SHA"');
|
|
25
|
+
expect(post.env!.TIP_SHA).toBe("${{ github.event.pull_request.base.sha }}");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("a stale-notes job on a push marks the open notes stale, naming the preview comment", () => {
|
|
29
|
+
const job = doc.jobs["stale-notes"]!;
|
|
30
|
+
expect(job.if).toBe("github.event_name == 'push' && startsWith(github.ref, 'refs/heads/')");
|
|
31
|
+
expect(job.permissions).toEqual({ contents: "read", "pull-requests": "write", statuses: "write" });
|
|
32
|
+
const step = job.steps.at(-1)!;
|
|
33
|
+
expect(step.run).toBe('npx --no-install plywood components pr-post --stale --tip "$TIP_SHA" --env prod --forge github --preview-comment');
|
|
34
|
+
expect(step.env).toEqual({ TIP_SHA: "${{ github.sha }}", GITHUB_TOKEN: "${{ github.token }}" });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("without staleNotes, no job and no tip", () => {
|
|
38
|
+
const plain = gen({ words: WORDS });
|
|
39
|
+
expect(plain.jobs["stale-notes"]).toBeUndefined();
|
|
40
|
+
expect(plain.jobs["plan-note"]!.steps.at(-1)!.run).not.toContain("--tip");
|
|
41
|
+
});
|
|
42
|
+
});
|