@intentius/chant-lexicon-github 0.124.22 → 0.124.24
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 +7 -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/__golden__/op-waves-jobs.yml +135 -0
- package/src/components/generate-op-waves-pipeline.test.ts +47 -0
- package/src/components/generate-op-waves-pipeline.ts +31 -11
|
@@ -27,6 +27,13 @@
|
|
|
27
27
|
* With `notify` on the spec (#3761), the workflow maps each variable it names
|
|
28
28
|
* from the repository secret of the same name, so a wave job can post to
|
|
29
29
|
* Slack, Teams or a signed webhook. `options.variables` wins over the mapping.
|
|
30
|
+
*
|
|
31
|
+
* `runner` (on the spec, a wave or a job group, #3914) is the job's `runs-on`.
|
|
32
|
+
* A wave's `jobs` renders its deciding job with the wave's own settings and a
|
|
33
|
+
* job per group (per share of a group) with the group's: its runner,
|
|
34
|
+
* environment, variables over the wave's, setup after the wave's, and id
|
|
35
|
+
* tokens. A job with id tokens gets `permissions:` of its own, the
|
|
36
|
+
* workflow's plus `id-token: write`, and each audience as `<NAME>_AUDIENCE`.
|
|
30
37
|
*/
|
|
31
38
|
import { type OpWavesSpec } from "@intentius/chant/op/op-waves";
|
|
32
39
|
import type { OpWavesPipelineOptions, OpWavesPipelineResult } from "@intentius/chant/lexicon";
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAAoE,KAAK,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAClI,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAgB9F,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAEtF,yFAAyF;AACzF,eAAO,MAAM,cAAc,YAAW,CAAC;AAEvC,oEAAoE;AACpE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1F;AAED,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,CA2DpI;AA0CD,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": "ab329d49ebddfa547afccf09a39cb746378211e9292b9d6f88db95d5f5f357e0",
|
|
5
5
|
"meta.json": "c50e0ef3845c370c3ad2210b8bba455980417c0f818aa6ed46e06d4fb8b1b9f9",
|
|
6
6
|
"types/index.d.ts": "0ead95536e947ddfb98a5acf0ddb88547913b5a53a342df67711c59266f84b3e",
|
|
7
7
|
"rules/deprecated-action-version.ts": "a9ba7cc924ff1c7055110416d8989273690415bfb5e8dcc32dc9431b9351f5ca",
|
|
@@ -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": "680c4d4083a0fe1a921a6bd565971dec440c393e60b6dbdeec3564d46567810f"
|
|
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.124.
|
|
3
|
+
"version": "0.124.24",
|
|
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,6 +61,6 @@
|
|
|
61
61
|
"typescript": "^5.9.3"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@intentius/chant": "^0.124.
|
|
64
|
+
"@intentius/chant": "^0.124.24"
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
name: migrations
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
workflow_dispatch: {}
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: migrations
|
|
11
|
+
cancel-in-progress: false
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
wave-1-dev:
|
|
18
|
+
runs-on:
|
|
19
|
+
- self-hosted
|
|
20
|
+
- dev
|
|
21
|
+
container: node:22
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v7
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
- name: Plan, gate and apply wave 1 (dev)
|
|
27
|
+
run: chant run wave --spec waves.json --wave 1
|
|
28
|
+
wave-2-prod-decide:
|
|
29
|
+
runs-on: plan
|
|
30
|
+
needs:
|
|
31
|
+
- wave-1-dev
|
|
32
|
+
container: node:22
|
|
33
|
+
permissions:
|
|
34
|
+
contents: write
|
|
35
|
+
id-token: write
|
|
36
|
+
env:
|
|
37
|
+
STAGE: apply
|
|
38
|
+
PLAN_TOKEN_AUDIENCE: plan.example
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v7
|
|
41
|
+
with:
|
|
42
|
+
fetch-depth: 0
|
|
43
|
+
- name: Plan wave 2 (prod) and decide its gate
|
|
44
|
+
run: chant run wave --spec waves.json --wave 2 --decide
|
|
45
|
+
- name: Keep wave 2's decision
|
|
46
|
+
if: always()
|
|
47
|
+
uses: actions/upload-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: migrations-wave-2-decision
|
|
50
|
+
path: .chant/op-waves/migrations/wave-2.json
|
|
51
|
+
if-no-files-found: ignore
|
|
52
|
+
include-hidden-files: true
|
|
53
|
+
overwrite: true
|
|
54
|
+
wave-2-prod-us-share-1:
|
|
55
|
+
runs-on:
|
|
56
|
+
group: prod-apply
|
|
57
|
+
labels:
|
|
58
|
+
- linux
|
|
59
|
+
needs:
|
|
60
|
+
- wave-2-prod-decide
|
|
61
|
+
container: node:22
|
|
62
|
+
environment:
|
|
63
|
+
name: prod-us
|
|
64
|
+
permissions:
|
|
65
|
+
contents: write
|
|
66
|
+
id-token: write
|
|
67
|
+
env:
|
|
68
|
+
STAGE: apply
|
|
69
|
+
REGION: us
|
|
70
|
+
CLOUD_TOKEN_AUDIENCE: us.example
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@v7
|
|
73
|
+
with:
|
|
74
|
+
fetch-depth: 0
|
|
75
|
+
- name: Download wave 2's decision
|
|
76
|
+
uses: actions/download-artifact@v4
|
|
77
|
+
with:
|
|
78
|
+
name: migrations-wave-2-decision
|
|
79
|
+
path: .chant/op-waves/migrations
|
|
80
|
+
- run: echo us
|
|
81
|
+
- name: Apply share 1 of job group us of wave 2 (prod)
|
|
82
|
+
run: chant run wave --spec waves.json --wave 2 --group us --share 1
|
|
83
|
+
wave-2-prod-us-share-2:
|
|
84
|
+
runs-on:
|
|
85
|
+
group: prod-apply
|
|
86
|
+
labels:
|
|
87
|
+
- linux
|
|
88
|
+
needs:
|
|
89
|
+
- wave-2-prod-decide
|
|
90
|
+
container: node:22
|
|
91
|
+
environment:
|
|
92
|
+
name: prod-us
|
|
93
|
+
permissions:
|
|
94
|
+
contents: write
|
|
95
|
+
id-token: write
|
|
96
|
+
env:
|
|
97
|
+
STAGE: apply
|
|
98
|
+
REGION: us
|
|
99
|
+
CLOUD_TOKEN_AUDIENCE: us.example
|
|
100
|
+
steps:
|
|
101
|
+
- uses: actions/checkout@v7
|
|
102
|
+
with:
|
|
103
|
+
fetch-depth: 0
|
|
104
|
+
- name: Download wave 2's decision
|
|
105
|
+
uses: actions/download-artifact@v4
|
|
106
|
+
with:
|
|
107
|
+
name: migrations-wave-2-decision
|
|
108
|
+
path: .chant/op-waves/migrations
|
|
109
|
+
- run: echo us
|
|
110
|
+
- name: Apply share 2 of job group us of wave 2 (prod)
|
|
111
|
+
run: chant run wave --spec waves.json --wave 2 --group us --share 2
|
|
112
|
+
wave-2-prod-eu:
|
|
113
|
+
runs-on: eu-apply
|
|
114
|
+
needs:
|
|
115
|
+
- wave-2-prod-decide
|
|
116
|
+
container: node:22
|
|
117
|
+
environment:
|
|
118
|
+
name: prod-eu
|
|
119
|
+
permissions:
|
|
120
|
+
contents: write
|
|
121
|
+
id-token: write
|
|
122
|
+
env:
|
|
123
|
+
STAGE: apply
|
|
124
|
+
PLAN_TOKEN_AUDIENCE: plan.example
|
|
125
|
+
steps:
|
|
126
|
+
- uses: actions/checkout@v7
|
|
127
|
+
with:
|
|
128
|
+
fetch-depth: 0
|
|
129
|
+
- name: Download wave 2's decision
|
|
130
|
+
uses: actions/download-artifact@v4
|
|
131
|
+
with:
|
|
132
|
+
name: migrations-wave-2-decision
|
|
133
|
+
path: .chant/op-waves/migrations
|
|
134
|
+
- name: Apply job group eu of wave 2 (prod)
|
|
135
|
+
run: chant run wave --spec waves.json --wave 2 --group eu
|
|
@@ -74,3 +74,50 @@ describe("generateGithubOpWavesPipeline", () => {
|
|
|
74
74
|
expect(() => generateGithubOpWavesPipeline({ ...waveLevel, hooks: { after: { apply: [{ command: ["./bin/smoke"], onFailure: "approve" }] } } }, { specFile: "waves.json" })).toThrow(/nothing left to hold/);
|
|
75
75
|
});
|
|
76
76
|
});
|
|
77
|
+
|
|
78
|
+
describe("a runner per wave and job groups per wave (#3914)", () => {
|
|
79
|
+
const grouped: OpWavesSpec = {
|
|
80
|
+
...spec,
|
|
81
|
+
runner: "plan",
|
|
82
|
+
waves: [
|
|
83
|
+
{ name: "dev", runs: [{ target: "dev" }], gate: "never", runner: ["self-hosted", "dev"] },
|
|
84
|
+
{
|
|
85
|
+
name: "prod",
|
|
86
|
+
runs: [{ target: "us/a" }, { target: "us/b" }, { target: "eu/a" }],
|
|
87
|
+
variables: { STAGE: "apply" },
|
|
88
|
+
idTokens: { PLAN_TOKEN: "plan.example" },
|
|
89
|
+
jobs: [
|
|
90
|
+
{ name: "us", runs: ["us/*"], shares: 2, runner: { group: "prod-apply", labels: ["linux"] }, environment: { name: "prod-us" }, variables: { REGION: "us" }, setup: [{ run: "echo us" }], idTokens: { CLOUD_TOKEN: "us.example" } },
|
|
91
|
+
{ name: "eu", runs: ["eu/*"], runner: "eu-apply", environment: { name: "prod-eu" } },
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
test("the rendered pipeline matches its golden", async () => {
|
|
98
|
+
await expect(generateGithubOpWavesPipeline(grouped, { specFile: "waves.json" }).files[0]!.yaml).toMatchFileSnapshot("./__golden__/op-waves-jobs.yml");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("runs-on, environment, variables, id-token and the group's command on each job", () => {
|
|
102
|
+
const { files, jobs } = generateGithubOpWavesPipeline(grouped, { specFile: "waves.json" });
|
|
103
|
+
expect(jobs.map((j) => j.jobName)).toEqual(["wave-1-dev", "wave-2-prod-decide", "wave-2-prod-us-share-1", "wave-2-prod-us-share-2", "wave-2-prod-eu"]);
|
|
104
|
+
const yaml = files[0]!.yaml;
|
|
105
|
+
expect(yaml).toMatch(/wave-1-dev:\n\s+runs-on:\n\s+- self-hosted\n\s+- dev\n/);
|
|
106
|
+
expect(yaml).toMatch(
|
|
107
|
+
/wave-2-prod-decide:\n\s+runs-on: plan\n[\s\S]*?permissions:\n\s+contents: write\n\s+id-token: write\n\s+env:\n\s+STAGE: apply\n\s+PLAN_TOKEN_AUDIENCE: plan.example\n/,
|
|
108
|
+
);
|
|
109
|
+
expect(yaml).toMatch(
|
|
110
|
+
/wave-2-prod-us-share-1:\n\s+runs-on:\n\s+group: prod-apply\n\s+labels:\n\s+- linux\n\s+needs:\n\s+- wave-2-prod-decide\n\s+container: node:22\n\s+environment:\n\s+name: prod-us\n\s+permissions:\n\s+contents: write\n\s+id-token: write\n\s+env:\n\s+STAGE: apply\n\s+REGION: us\n\s+CLOUD_TOKEN_AUDIENCE: us.example\n/,
|
|
111
|
+
);
|
|
112
|
+
expect(yaml).toContain("- run: echo us\n - name: Apply share 1 of job group us of wave 2 (prod)\n run: chant run wave --spec waves.json --wave 2 --group us --share 1\n");
|
|
113
|
+
// A group with no id tokens of its own gets the wave's.
|
|
114
|
+
expect(yaml).toMatch(/wave-2-prod-eu:\n\s+runs-on: eu-apply\n[\s\S]*?name: prod-eu\n\s+permissions:\n\s+contents: write\n\s+id-token: write\n\s+env:\n\s+STAGE: apply\n\s+PLAN_TOKEN_AUDIENCE: plan.example\n/);
|
|
115
|
+
expect(yaml).toContain("run: chant run wave --spec waves.json --wave 2 --group eu\n");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("the spec's runner is the resume and record-plans jobs' too", () => {
|
|
119
|
+
const { files } = generateGithubOpWavesPipeline({ ...spec, runner: "plan", resume: { schedule: "*/10 * * * *" }, waves: spec.waves.map((w) => ({ ...w, approval: "pr-review" as const })) }, { specFile: "waves.json" });
|
|
120
|
+
expect(files.map((f) => f.name)).toEqual(["migrations.yml", "migrations-resume.yml", "migrations-plans.yml"]);
|
|
121
|
+
for (const f of files) expect(f.yaml).not.toContain("ubuntu-latest");
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -27,9 +27,16 @@
|
|
|
27
27
|
* With `notify` on the spec (#3761), the workflow maps each variable it names
|
|
28
28
|
* from the repository secret of the same name, so a wave job can post to
|
|
29
29
|
* Slack, Teams or a signed webhook. `options.variables` wins over the mapping.
|
|
30
|
+
*
|
|
31
|
+
* `runner` (on the spec, a wave or a job group, #3914) is the job's `runs-on`.
|
|
32
|
+
* A wave's `jobs` renders its deciding job with the wave's own settings and a
|
|
33
|
+
* job per group (per share of a group) with the group's: its runner,
|
|
34
|
+
* environment, variables over the wave's, setup after the wave's, and id
|
|
35
|
+
* tokens. A job with id tokens gets `permissions:` of its own, the
|
|
36
|
+
* workflow's plus `id-token: write`, and each audience as `<NAME>_AUDIENCE`.
|
|
30
37
|
*/
|
|
31
38
|
|
|
32
|
-
import { notifyVariables, opWaveJobs, opWavesResumeCommand, type OpWavesSpec } from "@intentius/chant/op/op-waves";
|
|
39
|
+
import { notifyVariables, opWaveGroupOf, opWaveJobs, opWavesResumeCommand, type OpWavesSpec } from "@intentius/chant/op/op-waves";
|
|
33
40
|
import type { OpWavesPipelineOptions, OpWavesPipelineResult } from "@intentius/chant/lexicon";
|
|
34
41
|
import {
|
|
35
42
|
assertEnvironment,
|
|
@@ -38,8 +45,9 @@ import {
|
|
|
38
45
|
dirnameOf,
|
|
39
46
|
environmentDoc,
|
|
40
47
|
GITHUB_JOB_TOKEN,
|
|
41
|
-
|
|
48
|
+
githubRunsOn,
|
|
42
49
|
githubSecretEnv,
|
|
50
|
+
idTokenAudienceEnv,
|
|
43
51
|
nonEmpty,
|
|
44
52
|
pushBranches,
|
|
45
53
|
setupStepDoc,
|
|
@@ -66,27 +74,38 @@ export function buildGithubOpWavesDoc(
|
|
|
66
74
|
for (const wave of spec.waves) {
|
|
67
75
|
assertSetupSteps(`${spec.name}/${wave.name}`, wave.setup ?? []);
|
|
68
76
|
if (wave.environment) assertEnvironment(`${spec.name}/${wave.name}`, wave.environment);
|
|
77
|
+
for (const group of wave.jobs ?? []) {
|
|
78
|
+
assertSetupSteps(`${spec.name}/${wave.name}/${group.name}`, group.setup ?? []);
|
|
79
|
+
if (group.environment) assertEnvironment(`${spec.name}/${wave.name}/${group.name}`, group.environment);
|
|
80
|
+
}
|
|
69
81
|
}
|
|
82
|
+
const permissions = { contents: "write", ...(byReview ? { "pull-requests": "read" } : {}) };
|
|
70
83
|
|
|
71
84
|
const jobsDoc: Record<string, unknown> = {};
|
|
72
85
|
for (const job of jobs) {
|
|
73
86
|
const wave = spec.waves[job.wave - 1]!;
|
|
87
|
+
// #3914: a job group's job runs with the group's settings over the wave's.
|
|
88
|
+
const group = opWaveGroupOf(spec, job);
|
|
74
89
|
const steps: Array<Record<string, unknown>> = [checkoutStep(true)];
|
|
75
90
|
if (job.kind === "share") {
|
|
76
91
|
steps.push(downloadArtifactStep(`Download wave ${job.wave}'s decision`, opWaveRecordArtifact(spec, job.wave), dirnameOf(job.record!)));
|
|
77
92
|
}
|
|
78
|
-
steps.push(...(wave.setup ?? []).map(setupStepDoc), ...runSteps(options.beforeScript ?? []));
|
|
79
|
-
steps.push({ name: describeJob(job.kind, job.wave, wave.name, job.share), run: job.command.join(" ") });
|
|
93
|
+
steps.push(...[...(wave.setup ?? []), ...(group?.setup ?? [])].map(setupStepDoc), ...runSteps(options.beforeScript ?? []));
|
|
94
|
+
steps.push({ name: describeJob(job.kind, job.wave, wave.name, job.share, job.group), run: job.command.join(" ") });
|
|
80
95
|
steps.push(...runSteps(options.extraScript ?? []));
|
|
81
96
|
if (job.kind === "decide") {
|
|
82
97
|
steps.push(keepRecordStep(`Keep wave ${job.wave}'s decision`, opWaveRecordArtifact(spec, job.wave), job.record!));
|
|
83
98
|
}
|
|
99
|
+
const environment = group?.environment ?? wave.environment;
|
|
100
|
+
const idTokens = group?.idTokens ?? wave.idTokens;
|
|
84
101
|
jobsDoc[job.jobName] = {
|
|
85
|
-
"runs-on":
|
|
102
|
+
"runs-on": githubRunsOn(group?.runner ?? wave.runner ?? spec.runner),
|
|
86
103
|
...(job.needs.length > 0 ? { needs: job.needs } : {}),
|
|
87
104
|
container: image,
|
|
88
|
-
...(
|
|
89
|
-
|
|
105
|
+
...(environment ? { environment: environmentDoc(environment) } : {}),
|
|
106
|
+
// #3914: a job's own permissions replace the workflow's, so they repeat them.
|
|
107
|
+
...(idTokens ? { permissions: { ...permissions, "id-token": "write" } } : {}),
|
|
108
|
+
...nonEmpty("env", group || idTokens ? { ...wave.variables, ...group?.variables, ...idTokenAudienceEnv(idTokens) } : wave.variables),
|
|
90
109
|
steps,
|
|
91
110
|
};
|
|
92
111
|
}
|
|
@@ -98,7 +117,7 @@ export function buildGithubOpWavesDoc(
|
|
|
98
117
|
on: { push: { branches: pushBranches(spec.branches) }, workflow_dispatch: {} },
|
|
99
118
|
...nonEmpty("env", env),
|
|
100
119
|
concurrency: concurrency(spec.name),
|
|
101
|
-
permissions
|
|
120
|
+
permissions,
|
|
102
121
|
jobsDoc,
|
|
103
122
|
};
|
|
104
123
|
return {
|
|
@@ -123,7 +142,7 @@ function buildResumeDoc(spec: OpWavesSpec, options: OpWavesPipelineOptions): Git
|
|
|
123
142
|
concurrency: concurrency(`${spec.name}-resume`, true),
|
|
124
143
|
// contents: read fetches chant/lifecycle; actions: write re-runs a waiting run.
|
|
125
144
|
permissions: { contents: "read", actions: "write" },
|
|
126
|
-
jobsDoc: { resume: { "runs-on":
|
|
145
|
+
jobsDoc: { resume: { "runs-on": githubRunsOn(spec.runner), container: options.image ?? OP_WAVES_IMAGE, steps } },
|
|
127
146
|
};
|
|
128
147
|
}
|
|
129
148
|
|
|
@@ -138,12 +157,13 @@ function buildPlansDoc(spec: OpWavesSpec, options: OpWavesPipelineOptions): Gith
|
|
|
138
157
|
concurrency: concurrency(spec.name + "-plans-${{ github.event.number }}", true),
|
|
139
158
|
// contents: write pushes the record to chant/lifecycle.
|
|
140
159
|
permissions: { contents: "write" },
|
|
141
|
-
jobsDoc: { "record-plans": { "runs-on":
|
|
160
|
+
jobsDoc: { "record-plans": { "runs-on": githubRunsOn(spec.runner), container: options.image ?? OP_WAVES_IMAGE, steps } },
|
|
142
161
|
};
|
|
143
162
|
}
|
|
144
163
|
|
|
145
|
-
function describeJob(kind: "wave" | "decide" | "share", wave: number, name: string, share?: number): string {
|
|
164
|
+
function describeJob(kind: "wave" | "decide" | "share", wave: number, name: string, share?: number, group?: string): string {
|
|
146
165
|
if (kind === "decide") return `Plan wave ${wave} (${name}) and decide its gate`;
|
|
166
|
+
if (group !== undefined) return `Apply ${share !== undefined ? `share ${share} of ` : ""}job group ${group} of wave ${wave} (${name})`;
|
|
147
167
|
if (kind === "share") return `Apply share ${share} of wave ${wave} (${name})`;
|
|
148
168
|
return `Plan, gate and apply wave ${wave} (${name})`;
|
|
149
169
|
}
|