@intentius/chant-lexicon-github 0.117.0 → 0.118.1
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-waves-pipeline.d.ts +6 -0
- package/dist/components/generate-op-waves-pipeline.d.ts.map +1 -1
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/package.json +2 -2
- package/src/components/generate-op-waves-pipeline.test.ts +8 -0
- package/src/components/generate-op-waves-pipeline.ts +33 -4
|
@@ -18,6 +18,11 @@
|
|
|
18
18
|
* runs `chant run resume --op <name>` on that schedule: it re-runs the failed
|
|
19
19
|
* jobs of a run whose waiting wave has been approved since, with the job's
|
|
20
20
|
* token and `actions: write`.
|
|
21
|
+
*
|
|
22
|
+
* When a wave's approval is `pr-review` (#3684), the workflow also asks for
|
|
23
|
+
* `pull-requests: read`, so a wave can read the merged pull request's
|
|
24
|
+
* reviews, and a second workflow, `<name>-plans.yml`, runs `chant run wave
|
|
25
|
+
* --record-plans` on each pull request to record the digests its head plans.
|
|
21
26
|
*/
|
|
22
27
|
import { type OpWavesSpec } from "@intentius/chant/op/op-waves";
|
|
23
28
|
import type { OpWavesPipelineOptions, OpWavesPipelineResult } from "@intentius/chant/lexicon";
|
|
@@ -31,6 +36,7 @@ export declare function buildGithubOpWavesDoc(spec: OpWavesSpec, options: OpWave
|
|
|
31
36
|
doc: GithubOpPipelineDoc;
|
|
32
37
|
jobs: OpWavesPipelineResult["jobs"];
|
|
33
38
|
resumeDoc?: GithubOpPipelineDoc;
|
|
39
|
+
plansDoc?: GithubOpPipelineDoc;
|
|
34
40
|
};
|
|
35
41
|
/** Render an Op waves spec as one GitHub Actions workflow, `<name>.yml`. */
|
|
36
42
|
export declare function generateGithubOpWavesPipeline(spec: OpWavesSpec, options: OpWavesPipelineOptions): OpWavesPipelineResult;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-op-waves-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-waves-pipeline.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"generate-op-waves-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-waves-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAoC,KAAK,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAClG,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAE9F,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,wBAAwB,CAAC;AAEhC,yFAAyF;AACzF,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,oEAAoE;AACpE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1F;AAOD,qEAAqE;AACrE,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,sBAAsB,GAC9B;IAAE,GAAG,EAAE,mBAAmB,CAAC;IAAC,IAAI,EAAE,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAA;CAAE,CAgEpI;AA4CD,4EAA4E;AAC5E,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CAMvH"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "9af8fb73c9fc467e40ba29223cd21acd9b359cf19abc6da410bc9f398b2d930e",
|
|
5
5
|
"meta.json": "c50e0ef3845c370c3ad2210b8bba455980417c0f818aa6ed46e06d4fb8b1b9f9",
|
|
6
6
|
"types/index.d.ts": "bdd54a4145a39a9dd4f7bbc8c500191acb6bc9057a87da0040475afac84db397",
|
|
7
7
|
"rules/deprecated-action-version.ts": "d41e6e532ab7f623af1bee4ac5279fcb2baada7defa1c5d022a5bc71983e8797",
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
"skills/chant-github-patterns.md": "bb3abef289a8fdfcf07d6bb2d7289dcb2f38bc0cb0321ea320b78b45a6f548c0",
|
|
79
79
|
"skills/chant-github-security.md": "aab111cb0871cad30281ce48d7da23663689619351029219e2be019a1a61e394"
|
|
80
80
|
},
|
|
81
|
-
"composite": "
|
|
81
|
+
"composite": "138f25dc6c40d14fd2e8a099cc5823c7fcac2b600cee92cad3ad2cb06049707e"
|
|
82
82
|
}
|
package/dist/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.118.1",
|
|
4
4
|
"description": "GitHub Actions lexicon for chant — declarative IaC in TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"typescript": "^5.9.3"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@intentius/chant": "^0.
|
|
64
|
+
"@intentius/chant": "^0.118.1",
|
|
65
65
|
"typescript": "^5.9.3"
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -41,4 +41,12 @@ describe("generateGithubOpWavesPipeline", () => {
|
|
|
41
41
|
expect(yaml).toContain("GITHUB_TOKEN: '${{ github.token }}'");
|
|
42
42
|
expect(generateGithubOpWavesPipeline(spec, { specFile: "waves.json" }).files).toHaveLength(1);
|
|
43
43
|
});
|
|
44
|
+
|
|
45
|
+
test("a pr-review wave adds a pull request workflow that records the head's plans (#3684)", () => {
|
|
46
|
+
const { files } = generateGithubOpWavesPipeline({ ...spec, waves: spec.waves.map((w, i) => (i === spec.waves.length - 1 ? { ...w, approval: "pr-review" as const } : w)) }, { specFile: "waves.json" });
|
|
47
|
+
expect(files.map((f) => f.name)).toEqual([`${spec.name}.yml`, `${spec.name}-plans.yml`]);
|
|
48
|
+
expect(files[1]!.yaml).toContain("pull_request:");
|
|
49
|
+
expect(files[1]!.yaml).toContain("run: chant run wave --spec waves.json --record-plans");
|
|
50
|
+
expect(files[0]!.yaml).toMatch(/permissions:\n\s+contents: write\n\s+pull-requests: read/);
|
|
51
|
+
});
|
|
44
52
|
});
|
|
@@ -18,6 +18,11 @@
|
|
|
18
18
|
* runs `chant run resume --op <name>` on that schedule: it re-runs the failed
|
|
19
19
|
* jobs of a run whose waiting wave has been approved since, with the job's
|
|
20
20
|
* token and `actions: write`.
|
|
21
|
+
*
|
|
22
|
+
* When a wave's approval is `pr-review` (#3684), the workflow also asks for
|
|
23
|
+
* `pull-requests: read`, so a wave can read the merged pull request's
|
|
24
|
+
* reviews, and a second workflow, `<name>-plans.yml`, runs `chant run wave
|
|
25
|
+
* --record-plans` on each pull request to record the digests its head plans.
|
|
21
26
|
*/
|
|
22
27
|
|
|
23
28
|
import { opWaveJobs, opWavesResumeCommand, type OpWavesSpec } from "@intentius/chant/op/op-waves";
|
|
@@ -47,9 +52,10 @@ function dirOf(path: string): string {
|
|
|
47
52
|
export function buildGithubOpWavesDoc(
|
|
48
53
|
spec: OpWavesSpec,
|
|
49
54
|
options: OpWavesPipelineOptions,
|
|
50
|
-
): { doc: GithubOpPipelineDoc; jobs: OpWavesPipelineResult["jobs"]; resumeDoc?: GithubOpPipelineDoc } {
|
|
55
|
+
): { doc: GithubOpPipelineDoc; jobs: OpWavesPipelineResult["jobs"]; resumeDoc?: GithubOpPipelineDoc; plansDoc?: GithubOpPipelineDoc } {
|
|
51
56
|
const jobs = opWaveJobs(spec, options.specFile);
|
|
52
57
|
const image = options.image ?? OP_WAVES_IMAGE;
|
|
58
|
+
const byReview = spec.waves.some((w) => w.approval === "pr-review");
|
|
53
59
|
for (const wave of spec.waves) {
|
|
54
60
|
assertSetupSteps(`${spec.name}/${wave.name}`, wave.setup ?? []);
|
|
55
61
|
if (wave.environment) assertEnvironment(`${spec.name}/${wave.name}`, wave.environment);
|
|
@@ -101,10 +107,15 @@ export function buildGithubOpWavesDoc(
|
|
|
101
107
|
on: { push: { branches: spec.branches?.length ? spec.branches : ["main"] }, workflow_dispatch: {} },
|
|
102
108
|
...(options.variables && Object.keys(options.variables).length > 0 ? { env: options.variables } : {}),
|
|
103
109
|
concurrency: { group: spec.name, "cancel-in-progress": false },
|
|
104
|
-
permissions: { contents: "write" },
|
|
110
|
+
permissions: { contents: "write", ...(byReview ? { "pull-requests": "read" } : {}) },
|
|
105
111
|
jobsDoc,
|
|
106
112
|
};
|
|
107
|
-
return {
|
|
113
|
+
return {
|
|
114
|
+
doc,
|
|
115
|
+
jobs,
|
|
116
|
+
...(spec.resume ? { resumeDoc: buildResumeDoc(spec, options) } : {}),
|
|
117
|
+
...(byReview ? { plansDoc: buildPlansDoc(spec, options) } : {}),
|
|
118
|
+
};
|
|
108
119
|
}
|
|
109
120
|
|
|
110
121
|
/** The scheduled resume workflow (#3683). */
|
|
@@ -126,6 +137,23 @@ function buildResumeDoc(spec: OpWavesSpec, options: OpWavesPipelineOptions): Git
|
|
|
126
137
|
};
|
|
127
138
|
}
|
|
128
139
|
|
|
140
|
+
/** The pull request workflow a `pr-review` wave needs (#3684): record the digests each head plans. */
|
|
141
|
+
function buildPlansDoc(spec: OpWavesSpec, options: OpWavesPipelineOptions): GithubOpPipelineDoc {
|
|
142
|
+
const steps: Array<Record<string, unknown>> = [{ uses: actionRef("actions/checkout"), with: { "fetch-depth": 0 } }];
|
|
143
|
+
for (const step of spec.waves[0]?.setup ?? []) steps.push({ ...step });
|
|
144
|
+
for (const line of options.beforeScript ?? []) steps.push({ run: line });
|
|
145
|
+
steps.push({ name: "Plan every wave at the head and record its digests", run: ["chant", "run", "wave", "--spec", options.specFile, "--record-plans"].join(" ") });
|
|
146
|
+
return {
|
|
147
|
+
name: `${spec.name}-plans`,
|
|
148
|
+
on: { pull_request: { branches: spec.branches?.length ? spec.branches : ["main"] } },
|
|
149
|
+
...(options.variables && Object.keys(options.variables).length > 0 ? { env: options.variables } : {}),
|
|
150
|
+
concurrency: { group: spec.name + "-plans-${{ github.event.number }}", "cancel-in-progress": true },
|
|
151
|
+
// contents: write pushes the record to chant/lifecycle.
|
|
152
|
+
permissions: { contents: "write" },
|
|
153
|
+
jobsDoc: { "record-plans": { "runs-on": "ubuntu-latest", container: options.image ?? OP_WAVES_IMAGE, steps } },
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
129
157
|
function describeJob(kind: "wave" | "decide" | "share", wave: number, name: string, share?: number): string {
|
|
130
158
|
if (kind === "decide") return `Plan wave ${wave} (${name}) and decide its gate`;
|
|
131
159
|
if (kind === "share") return `Apply share ${share} of wave ${wave} (${name})`;
|
|
@@ -134,8 +162,9 @@ function describeJob(kind: "wave" | "decide" | "share", wave: number, name: stri
|
|
|
134
162
|
|
|
135
163
|
/** Render an Op waves spec as one GitHub Actions workflow, `<name>.yml`. */
|
|
136
164
|
export function generateGithubOpWavesPipeline(spec: OpWavesSpec, options: OpWavesPipelineOptions): OpWavesPipelineResult {
|
|
137
|
-
const { doc, jobs, resumeDoc } = buildGithubOpWavesDoc(spec, options);
|
|
165
|
+
const { doc, jobs, resumeDoc, plansDoc } = buildGithubOpWavesDoc(spec, options);
|
|
138
166
|
const files = [{ name: `${spec.name}.yml`, yaml: emitOpPipelineYAML(doc) }];
|
|
139
167
|
if (resumeDoc) files.push({ name: `${spec.name}-resume.yml`, yaml: emitOpPipelineYAML(resumeDoc) });
|
|
168
|
+
if (plansDoc) files.push({ name: `${spec.name}-plans.yml`, yaml: emitOpPipelineYAML(plansDoc) });
|
|
140
169
|
return { files, jobs };
|
|
141
170
|
}
|