@intentius/chant-lexicon-github 0.124.54 → 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/agent-jobs.d.ts +67 -0
- package/dist/components/agent-jobs.d.ts.map +1 -0
- package/dist/components/agent-jobs.js +139 -0
- package/dist/components/control-pipeline.d.ts +34 -0
- package/dist/components/control-pipeline.d.ts.map +1 -0
- package/dist/components/control-pipeline.js +45 -0
- package/dist/components/generate-op-pipeline.d.ts +8 -1
- package/dist/components/generate-op-pipeline.d.ts.map +1 -1
- package/dist/components/generate-op-pipeline.js +144 -16
- package/dist/components/generate-op-waves-pipeline.d.ts +27 -1
- package/dist/components/generate-op-waves-pipeline.d.ts.map +1 -1
- package/dist/components/generate-op-waves-pipeline.js +135 -16
- package/dist/components/generate-pipeline.d.ts.map +1 -1
- package/dist/components/generate-pipeline.js +160 -15
- 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/agent-jobs.ts +177 -0
- package/src/components/control-pipeline.ts +65 -0
- package/src/components/generate-op-pipeline.agent.test.ts +147 -0
- package/src/components/generate-op-pipeline.cloud.test.ts +96 -0
- package/src/components/generate-op-pipeline.notice.test.ts +2 -1
- package/src/components/generate-op-pipeline.ts +165 -20
- package/src/components/generate-op-waves-pipeline.approval.test.ts +46 -0
- package/src/components/generate-op-waves-pipeline.control.test.ts +64 -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.test.ts +17 -0
- package/src/components/generate-op-waves-pipeline.ts +157 -15
- package/src/components/generate-pipeline.ts +170 -15
- package/src/components/generate-pr-pipeline.comment-locks.test.ts +53 -0
- package/src/components/generate-pr-pipeline.comment-preview.test.ts +64 -0
- package/src/components/generate-pr-pipeline.comment-targets.test.ts +24 -0
- package/src/components/generate-pr-pipeline.control.test.ts +94 -0
- package/src/components/generate-pr-pipeline.pulumi-identity.test.ts +43 -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
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AAEH,OAAO,EAYL,KAAK,WAAW,EACjB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAmB9F,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,4DAA4D;AAC5D,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE9F;AAED,qEAAqE;AACrE,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,sBAAsB,EAC/B,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;CAAO,GAC7C;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,CA4FpI;AAoID,4EAA4E;AAC5E,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CAMvH"}
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
* downloads it. The forgejo generator reuses {@link buildGithubOpWavesDoc}
|
|
23
23
|
* and applies its dialect.
|
|
24
24
|
*
|
|
25
|
+
* `options.bin` (#4607) is a product's command in place of `chant` in every
|
|
26
|
+
* job line: the wave jobs, the resume job and the plans job.
|
|
27
|
+
*
|
|
25
28
|
* With `resume` on the spec (#3683), a second workflow, `<name>-resume.yml`,
|
|
26
29
|
* runs `chant run resume --op <name>` on that schedule: it re-runs the failed
|
|
27
30
|
* jobs of a run whose waiting wave has been approved since, with the job's
|
|
@@ -31,6 +34,8 @@
|
|
|
31
34
|
* `pull-requests: read`, so a wave can read the merged pull request's
|
|
32
35
|
* reviews, and a second workflow, `<name>-plans.yml`, runs `chant run wave
|
|
33
36
|
* --record-plans` on each pull request to record the digests its head plans.
|
|
37
|
+
* A `pull-request` wave (#4630) gets the plans workflow too, for the approval
|
|
38
|
+
* status before merge; it asks for no `pull-requests: read` on the waves.
|
|
34
39
|
*
|
|
35
40
|
* With `notify` on the spec (#3761), the workflow maps each variable it names
|
|
36
41
|
* from the repository secret of the same name, so a wave job can post to
|
|
@@ -49,10 +54,32 @@
|
|
|
49
54
|
* checkout. Every later run step starts with an unset of the variables a
|
|
50
55
|
* token is requested with, so the code the job runs holds no OIDC token
|
|
51
56
|
* and cannot request one for another group's identity.
|
|
57
|
+
*
|
|
58
|
+
* With `control` on the spec (#4617), a `control-dispatch` job needs the
|
|
59
|
+
* last wave's jobs and runs after them unless the run was cancelled, so it
|
|
60
|
+
* runs after a failure too. It downloads every job's outcome file, adds
|
|
61
|
+
* them up with `chant run wave --waves-outcome` (through the product's
|
|
62
|
+
* bin) into `CHANT_CONTROL_OUTCOME`, and sends the control repo one
|
|
63
|
+
* dispatch carrying that outcome and the pushed commit. Only the dispatch
|
|
64
|
+
* step holds the control token: the `tokenSecret` secret, mapped into
|
|
65
|
+
* `CHANT_CONTROL_TOKEN` and handed to curl on its stdin
|
|
66
|
+
* (`controlDispatchScript`, #4568).
|
|
67
|
+
*
|
|
68
|
+
* Every wave, deciding and share job starts with the apply-job guard
|
|
69
|
+
* (#4624, `@intentius/chant/ci-job-guard`), before the trade and the
|
|
70
|
+
* checkout: it stops the job unless `GITHUB_REF` is one of the spec's
|
|
71
|
+
* branches, the job runs in its forge environment (read from an OIDC
|
|
72
|
+
* token's claims, so only in a job that may request one), and, for a
|
|
73
|
+
* runner whose labels include `self-hosted`, on a self-hosted runner. The
|
|
74
|
+
* forgejo generator passes `forge: "forgejo"`, which checks the ref and
|
|
75
|
+
* runner only.
|
|
52
76
|
*/
|
|
53
|
-
import { notifyVariables, opWaveGroupOf, opWaveJobs, opWaveOutcomeDir, opWavesResumeCommand } from "@intentius/chant/op/op-waves";
|
|
77
|
+
import { notifyVariables, opWaveGroupOf, opWaveJobs, opWaveOutcomeDir, opWavesControlTarget, opWavesLastJobs, opWavesApprovalStatusCommand, opWavesPostsApprovalStatus, opWavesOutcomeCommand, opWavesRecordPlansCommand, opWavesResumeCommand, } from "@intentius/chant/op/op-waves";
|
|
78
|
+
import { CONTROL_OUTCOME_VAR, controlDispatchJob } from "@intentius/chant/control/dispatch";
|
|
54
79
|
import { assertEnvironment, assertSetupSteps, CI_IMAGE, dirnameOf, environmentDoc, GITHUB_JOB_TOKEN, githubRunsOn, githubSecretEnv, idTokenAudienceEnv, nonEmpty, pushBranches, setupStepDoc, } from "@intentius/chant/ci-jobs";
|
|
55
80
|
import { OIDC_REQUEST_VARS, TRADE_SPEC_ENV, tradeLine } from "@intentius/chant/ci-identity";
|
|
81
|
+
import { JOB_GUARD_ENV, jobGuardLine, jobGuardSpec, opWaveJobLabel } from "@intentius/chant/ci-job-guard";
|
|
82
|
+
import { resolveProductWords } from "@intentius/chant/ci-product-words";
|
|
56
83
|
import { checkoutStep, concurrency, DOWNLOAD_ARTIFACT, downloadArtifactStep, keepRecordStep, runSteps } from "./ci-jobs.js";
|
|
57
84
|
import { emitOpPipelineYAML } from "./generate-op-pipeline.js";
|
|
58
85
|
/** The default image: it carries git, which the runner needs to read the base commit. */
|
|
@@ -66,10 +93,14 @@ export function opWaveOutcomeArtifact(spec, jobName) {
|
|
|
66
93
|
return `${spec.name}-${jobName}-outcome`;
|
|
67
94
|
}
|
|
68
95
|
/** The workflow document, before emission, and the jobs it holds. */
|
|
69
|
-
export function buildGithubOpWavesDoc(spec, options) {
|
|
70
|
-
const jobs = opWaveJobs(spec, options.specFile);
|
|
96
|
+
export function buildGithubOpWavesDoc(spec, options, dialect = {}) {
|
|
97
|
+
const jobs = opWaveJobs(spec, options.specFile, { bin: options.bin });
|
|
98
|
+
const forge = dialect.forge ?? "github";
|
|
99
|
+
const product = resolveProductWords(options.words ?? {}).product;
|
|
71
100
|
const image = options.image ?? OP_WAVES_IMAGE;
|
|
72
101
|
const byReview = spec.waves.some((w) => w.approval === "pr-review");
|
|
102
|
+
// #4630: a pr-review or pull-request wave gets the plans workflow and its approval status.
|
|
103
|
+
const statusBeforeMerge = opWavesPostsApprovalStatus(spec);
|
|
73
104
|
for (const wave of spec.waves) {
|
|
74
105
|
assertSetupSteps(`${spec.name}/${wave.name}`, wave.setup ?? []);
|
|
75
106
|
if (wave.environment)
|
|
@@ -88,7 +119,21 @@ export function buildGithubOpWavesDoc(spec, options) {
|
|
|
88
119
|
const group = opWaveGroupOf(spec, job);
|
|
89
120
|
// #4332: the job's cloud identities, traded before the checkout.
|
|
90
121
|
const cloud = group?.cloud ?? wave.cloud;
|
|
91
|
-
const
|
|
122
|
+
const environment = group?.environment ?? wave.environment;
|
|
123
|
+
const idTokens = group?.idTokens ?? wave.idTokens;
|
|
124
|
+
const idToken = idTokens !== undefined || cloud !== undefined;
|
|
125
|
+
const runner = group?.runner ?? wave.runner ?? spec.runner;
|
|
126
|
+
// #4624: the guard first, before the trade and the checkout.
|
|
127
|
+
const guard = guardStep({
|
|
128
|
+
forge,
|
|
129
|
+
job: opWaveJobLabel(spec.name, job),
|
|
130
|
+
product,
|
|
131
|
+
...(environment && forge === "github" ? { environment: environment.name } : {}),
|
|
132
|
+
branches: pushBranches(spec.branches),
|
|
133
|
+
...(runner !== undefined ? { runner } : {}),
|
|
134
|
+
idToken,
|
|
135
|
+
});
|
|
136
|
+
const steps = [guard, ...(cloud ? [tradeStep(cloud)] : []), checkoutStep(true)];
|
|
92
137
|
if (job.kind === "share") {
|
|
93
138
|
steps.push(downloadArtifactStep(`Download wave ${job.wave}'s decision`, opWaveRecordArtifact(spec, job.wave), dirnameOf(job.record)));
|
|
94
139
|
}
|
|
@@ -105,11 +150,8 @@ export function buildGithubOpWavesDoc(spec, options) {
|
|
|
105
150
|
steps.push(keepRecordStep(`Keep wave ${job.wave}'s decision`, opWaveRecordArtifact(spec, job.wave), job.record));
|
|
106
151
|
}
|
|
107
152
|
steps.push(keepRecordStep(`Keep the job's outcome`, opWaveOutcomeArtifact(spec, job.jobName), job.outcome));
|
|
108
|
-
const environment = group?.environment ?? wave.environment;
|
|
109
|
-
const idTokens = group?.idTokens ?? wave.idTokens;
|
|
110
|
-
const idToken = idTokens !== undefined || cloud !== undefined;
|
|
111
153
|
jobsDoc[job.jobName] = {
|
|
112
|
-
"runs-on": githubRunsOn(
|
|
154
|
+
"runs-on": githubRunsOn(runner),
|
|
113
155
|
...(job.needs.length > 0 ? { needs: job.needs } : {}),
|
|
114
156
|
// #4322: a wave's jobs run after a failure in the wave before; a share job runs only on its deciding job's success.
|
|
115
157
|
...(job.needs.length > 0 && job.kind !== "share" ? { if: "!cancelled()" } : {}),
|
|
@@ -121,6 +163,11 @@ export function buildGithubOpWavesDoc(spec, options) {
|
|
|
121
163
|
steps,
|
|
122
164
|
};
|
|
123
165
|
}
|
|
166
|
+
// #4617: once the last wave ran, one job tells the control repo how the waves ended.
|
|
167
|
+
if (spec.control) {
|
|
168
|
+
const [name, job] = controlJob(spec, options, jobs, image, permissions);
|
|
169
|
+
jobsDoc[name] = job;
|
|
170
|
+
}
|
|
124
171
|
// #3761: the notify variables come from the secrets of the same name.
|
|
125
172
|
const env = { ...githubSecretEnv(notifyVariables(spec.notify)), ...(options.variables ?? {}) };
|
|
126
173
|
const doc = {
|
|
@@ -135,15 +182,54 @@ export function buildGithubOpWavesDoc(spec, options) {
|
|
|
135
182
|
doc,
|
|
136
183
|
jobs,
|
|
137
184
|
...(spec.resume ? { resumeDoc: buildResumeDoc(spec, options) } : {}),
|
|
138
|
-
...(
|
|
185
|
+
...(statusBeforeMerge ? { plansDoc: buildPlansDoc(spec, options, forge) } : {}),
|
|
139
186
|
};
|
|
140
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* The control dispatch job (#4617): after the last wave, whatever it did,
|
|
190
|
+
* the waves' outcome read from every job's outcome file, then one dispatch
|
|
191
|
+
* carrying it. The pushed commit is the one every wave ran at.
|
|
192
|
+
*/
|
|
193
|
+
function controlJob(spec, options, jobs, image, permissions) {
|
|
194
|
+
const control = spec.control;
|
|
195
|
+
const dispatch = controlDispatchJob("github", opWavesControlTarget(control), "${{ github.sha }}", {
|
|
196
|
+
...(options.words ? { words: options.words } : {}),
|
|
197
|
+
...(control.sentCheck ? { sentCheck: control.sentCheck } : {}),
|
|
198
|
+
...(control.markSent ? { markSent: control.markSent } : {}),
|
|
199
|
+
outcome: true,
|
|
200
|
+
});
|
|
201
|
+
const marks = control.sentCheck !== undefined || control.markSent !== undefined;
|
|
202
|
+
const steps = [
|
|
203
|
+
checkoutStep(true),
|
|
204
|
+
{ name: "Download the waves' outcomes", uses: DOWNLOAD_ARTIFACT, with: { path: opWaveOutcomeDir(spec) } },
|
|
205
|
+
...(spec.waves[0]?.setup ?? []).map(setupStepDoc),
|
|
206
|
+
...runSteps(options.beforeScript ?? []),
|
|
207
|
+
{
|
|
208
|
+
name: "Add up the waves' outcomes",
|
|
209
|
+
run: `outcome="$(${opWavesOutcomeCommand(options.specFile, options.bin).join(" ")})" || exit 1\necho "${CONTROL_OUTCOME_VAR}=$outcome" >> "$GITHUB_ENV"`,
|
|
210
|
+
},
|
|
211
|
+
{ name: `Tell the control repo ${control.repo} how ${control.member}'s waves ended`, env: { ...dispatch.env, ...(marks ? { GITHUB_TOKEN: GITHUB_JOB_TOKEN } : {}) }, run: dispatch.script },
|
|
212
|
+
];
|
|
213
|
+
return [
|
|
214
|
+
dispatch.name,
|
|
215
|
+
{
|
|
216
|
+
"runs-on": githubRunsOn(spec.runner),
|
|
217
|
+
needs: opWavesLastJobs(jobs),
|
|
218
|
+
// The waves ran, whatever they did: a failed run is an outcome the control repo is told about.
|
|
219
|
+
if: "!cancelled()",
|
|
220
|
+
container: image,
|
|
221
|
+
// markSent sets a commit status; the rest reads the repository and the run's artifacts.
|
|
222
|
+
permissions: marks ? { ...permissions, statuses: "write" } : permissions,
|
|
223
|
+
steps,
|
|
224
|
+
},
|
|
225
|
+
];
|
|
226
|
+
}
|
|
141
227
|
/** The scheduled resume workflow (#3683). */
|
|
142
228
|
function buildResumeDoc(spec, options) {
|
|
143
229
|
const steps = [checkoutStep(), ...runSteps(options.beforeScript ?? [])];
|
|
144
230
|
steps.push({
|
|
145
231
|
name: `Resume the waves of ${spec.name} whose approval arrived`,
|
|
146
|
-
run: opWavesResumeCommand(spec).join(" "),
|
|
232
|
+
run: opWavesResumeCommand(spec, options.bin).join(" "),
|
|
147
233
|
env: { GITHUB_TOKEN: GITHUB_JOB_TOKEN },
|
|
148
234
|
});
|
|
149
235
|
return {
|
|
@@ -155,20 +241,53 @@ function buildResumeDoc(spec, options) {
|
|
|
155
241
|
jobsDoc: { resume: { "runs-on": githubRunsOn(spec.runner), container: options.image ?? OP_WAVES_IMAGE, steps } },
|
|
156
242
|
};
|
|
157
243
|
}
|
|
158
|
-
/**
|
|
159
|
-
|
|
244
|
+
/**
|
|
245
|
+
* The pull request workflow a `pr-review` wave needs (#3684): record the
|
|
246
|
+
* digests each head plans. The `approval-status` job after it (#4630) posts
|
|
247
|
+
* `<product>/approval` on the head, and runs alone on each review submitted
|
|
248
|
+
* or dismissed. It holds a read token that may write statuses and nothing
|
|
249
|
+
* else. A `pull-request` wave gets the same workflow: the record keeps its
|
|
250
|
+
* change set, and the status reads the pull request's own approval of it.
|
|
251
|
+
*/
|
|
252
|
+
function buildPlansDoc(spec, options, forge) {
|
|
160
253
|
const steps = [checkoutStep(true), ...(spec.waves[0]?.setup ?? []).map(setupStepDoc), ...runSteps(options.beforeScript ?? [])];
|
|
161
|
-
steps.push({ name: "Plan every wave at the head and record its digests", run:
|
|
254
|
+
steps.push({ name: "Plan every wave at the head and record its digests", run: opWavesRecordPlansCommand(options.specFile, options.bin).join(" ") });
|
|
255
|
+
const statusSteps = [checkoutStep(true), ...runSteps(options.beforeScript ?? [])];
|
|
256
|
+
statusSteps.push({
|
|
257
|
+
name: "Post the approval status on the head",
|
|
258
|
+
run: opWavesApprovalStatusCommand(options.specFile, forge, options.bin).join(" "),
|
|
259
|
+
env: {
|
|
260
|
+
GITHUB_TOKEN: GITHUB_JOB_TOKEN,
|
|
261
|
+
PR_NUMBER: "${{ github.event.pull_request.number }}",
|
|
262
|
+
HEAD_SHA: "${{ github.event.pull_request.head.sha }}",
|
|
263
|
+
BASE_SHA: "${{ github.event.pull_request.base.sha }}",
|
|
264
|
+
},
|
|
265
|
+
});
|
|
162
266
|
return {
|
|
163
267
|
name: `${spec.name}-plans`,
|
|
164
|
-
on: { pull_request: { branches: pushBranches(spec.branches) } },
|
|
268
|
+
on: { pull_request: { branches: pushBranches(spec.branches) }, pull_request_review: { types: ["submitted", "dismissed"] } },
|
|
165
269
|
...nonEmpty("env", options.variables),
|
|
166
|
-
concurrency: concurrency(spec.name + "-plans-${{ github.event.number }}", true),
|
|
270
|
+
concurrency: concurrency(spec.name + "-plans-${{ github.event_name }}-${{ github.event.pull_request.number }}", true),
|
|
167
271
|
// contents: write pushes the record to chant/lifecycle.
|
|
168
272
|
permissions: { contents: "write" },
|
|
169
|
-
jobsDoc: {
|
|
273
|
+
jobsDoc: {
|
|
274
|
+
"record-plans": { if: "github.event_name == 'pull_request'", "runs-on": githubRunsOn(spec.runner), container: options.image ?? OP_WAVES_IMAGE, steps },
|
|
275
|
+
"approval-status": {
|
|
276
|
+
needs: ["record-plans"],
|
|
277
|
+
// On a review the record-plans job is skipped, and the status reads the plans an earlier push recorded.
|
|
278
|
+
if: "${{ always() && (needs.record-plans.result == 'success' || needs.record-plans.result == 'skipped') }}",
|
|
279
|
+
"runs-on": githubRunsOn(spec.runner),
|
|
280
|
+
container: options.image ?? OP_WAVES_IMAGE,
|
|
281
|
+
permissions: { contents: "read", "pull-requests": "read", statuses: "write" },
|
|
282
|
+
steps: statusSteps,
|
|
283
|
+
},
|
|
284
|
+
},
|
|
170
285
|
};
|
|
171
286
|
}
|
|
287
|
+
/** The apply-job guard step (#4624): the job's expectation in CHANT_JOB_GUARD, checked by `node -e` from `/`. */
|
|
288
|
+
function guardStep(input) {
|
|
289
|
+
return { name: "Check the job runs where the spec set it to run", env: { [JOB_GUARD_ENV]: JSON.stringify(jobGuardSpec(input)) }, run: jobGuardLine() };
|
|
290
|
+
}
|
|
172
291
|
/** The step that trades the job's OIDC token for `cloud` (#4332), writing the credentials to GITHUB_ENV. */
|
|
173
292
|
function tradeStep(cloud) {
|
|
174
293
|
return { name: "Trade the job's OIDC token for its cloud identities", env: { [TRADE_SPEC_ENV]: JSON.stringify(cloud) }, run: tradeLine("$GITHUB_ENV", "github-env") };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAEH,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"generate-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAEH,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,oCAAoC,CAAC;AA2CjG,OAAO,KAAK,EACV,oBAAoB,IAAI,YAAY,EACpC,wBAAwB,IAAI,qBAAqB,EACjD,uBAAuB,IAAI,oBAAoB,EAEhD,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,CAAC;AAE1E;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,6FAA6F;IAC7F,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5B,mIAAmI;IACnI,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,uEAAuE;IACvE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0DAA0D;IAC1D,IAAI,EAAE,YAAY,EAAE,CAAC;CACtB;AAgoBD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAWpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,eAAe,EAAE,EAC7B,OAAO,GAAE,qBAA0B,GAClC,iBAAiB,CA+JnB;AA4CD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,eAAe,EAAE,EAC7B,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,CAGtB"}
|
|
@@ -58,14 +58,19 @@
|
|
|
58
58
|
import { resolveComponentGraph } from "@intentius/chant/components/driver";
|
|
59
59
|
import { hasPublishStep, promoteArchivePaths } from "@intentius/chant/components/promote";
|
|
60
60
|
import { GATED_WAVE_RECORD, gatedWaveJobs } from "@intentius/chant/components/gated-wave-pipeline";
|
|
61
|
-
import { CI_IMAGE, ciJobName, dirnameOf, fillCommand, GITHUB_JOB_TOKEN, GITHUB_RUNNER } from "@intentius/chant/ci-jobs";
|
|
61
|
+
import { CI_IMAGE, ciJobName, dirnameOf, fillCommand, GITHUB_JOB_TOKEN, GITHUB_RUNNER, githubRunsOn } from "@intentius/chant/ci-jobs";
|
|
62
62
|
import { CHECKOUT_TOKEN_ENV, checkoutScript, dropForgeTokens, handoverOutputs, handoverTrap, takeOverScript } from "@intentius/chant/ci-handover";
|
|
63
63
|
import { OIDC_REQUEST_VARS, TRADE_SPEC_ENV, tradeLine } from "@intentius/chant/ci-identity";
|
|
64
|
-
import {
|
|
64
|
+
import { prApplyComment, GREEN_TAG_PATTERN, GREEN_TICK_WORKFLOW, PR_APPLY_RECORD, PR_LOOP_REPORT_DIR, prApplyGroup, prRecordGroup, prApplyRecordKey, prApplyWaveJobName, prLoopJobs, prLoopPostJob, prLoopSetup, PR_PLAN_NOTE_JOB, PR_LOCK_JOB, PR_UNLOCK_JOB, PR_UNLOCK_COMMAND, prLockCommand, prCommentLockJobs, prCommentPreviewJob, PREVIEW_REF_ENV, PREVIEW_HEAD_OUTPUT, prLoopWaveJobs, prLoopWithWaves, prEnvJobs, prLoopJobStage, prStaleJob, PR_STALE_JOB, prLoopRunner, PR_ENV_REPORT_DIR, PR_ENV_SWEEP_CRON, PR_ENV_POST_JOB, } from "@intentius/chant/components/pr-pipeline";
|
|
65
|
+
import { forgejoRepoLockLines, REPO_APPLY_GROUP } from "@intentius/chant/components/repo-lock";
|
|
65
66
|
import { memberRepoPath } from "@intentius/chant/lexicon";
|
|
67
|
+
import { productStatusContext, resolveProductWords } from "@intentius/chant/ci-product-words";
|
|
68
|
+
import { controlDispatchJob, controlNoteCheckout } from "@intentius/chant/control/dispatch";
|
|
66
69
|
import { afterEarlierWave, CACHE_RESTORE, CACHE_SAVE, CHECKOUT, checkoutStep, concurrency, downloadArtifactStep, emitWorkflowYAML, keepRecordStep, runSteps, UPLOAD_ARTIFACT } from "./ci-jobs.js";
|
|
67
70
|
/** The plan job's last step, whose outputs carry its report to the plan-note job (#4295). */
|
|
68
71
|
const PLAN_STEP_ID = "plan";
|
|
72
|
+
/** The comment preview's checkout step (#4620), whose output names the head it checked out. */
|
|
73
|
+
const PREVIEW_HEAD_STEP_ID = "head";
|
|
69
74
|
/** The output a gated apply wave hands the green commit it applies through, and the step that sets it (#4221). */
|
|
70
75
|
const WAVE_GREEN_OUTPUT = "green-sha";
|
|
71
76
|
const WAVE_STEP_ID = "apply";
|
|
@@ -203,10 +208,12 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
203
208
|
});
|
|
204
209
|
const withIdToken = (permissions) => (identity ? { ...permissions, "id-token": "write" } : permissions);
|
|
205
210
|
const reportName = (job) => `chant-pr-${job.wave ? prApplyWaveJobName(job.wave) : job.jobName}`;
|
|
211
|
+
// #4552: on the repo grain a Forgejo apply takes the lock tag in its own step, before the command, since the runner does not queue on a concurrency group.
|
|
212
|
+
const repoLock = (job) => (loop.lockGrain === "repo" && loop.forge === "forgejo" && job.jobName !== "record" ? forgejoRepoLockLines() : []);
|
|
206
213
|
const steps = (job, name) => [
|
|
207
214
|
...(identity ? [tradeStep(job.jobName === "record" ? identity.plan : identity.apply, job.jobName === "record" ? "plan" : "apply")] : []),
|
|
208
215
|
checkoutStep(true),
|
|
209
|
-
...runSteps(prLoopSetup(image)),
|
|
216
|
+
...runSteps(prLoopSetup(image, loop.engineSetup)),
|
|
210
217
|
...runSteps(beforeScript),
|
|
211
218
|
...(job.build ? [{ name: "Build the templates", run: job.build[0] }, { name: "Lint the project", run: job.build[1] }] : []),
|
|
212
219
|
...(job.jobName === "apply" ? [restoreRecord(job.wave)] : []),
|
|
@@ -218,9 +225,9 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
218
225
|
name,
|
|
219
226
|
id: WAVE_STEP_ID,
|
|
220
227
|
env: { ...job.run.env, ...(job.wave > 1 ? { GREEN_SHA: `\${{ needs.${prApplyWaveJobName(job.wave - 1)}.outputs.${WAVE_GREEN_OUTPUT} }}` } : {}) },
|
|
221
|
-
run: [...(job.checkout ?? []), `echo "${WAVE_GREEN_OUTPUT}=$GREEN_SHA" >> "$GITHUB_OUTPUT"`, ...(job.setup ?? []), job.run.command].join("\n"),
|
|
228
|
+
run: [...(job.checkout ?? []), `echo "${WAVE_GREEN_OUTPUT}=$GREEN_SHA" >> "$GITHUB_OUTPUT"`, ...(job.setup ?? []), ...repoLock(job), job.run.command].join("\n"),
|
|
222
229
|
}
|
|
223
|
-
: { name, env: job.run.env, run: [...(job.checkout ?? []), ...(job.setup ?? []), job.run.command].join("\n") },
|
|
230
|
+
: { name, env: job.run.env, run: [...(job.checkout ?? []), ...(job.setup ?? []), ...repoLock(job), job.run.command].join("\n") },
|
|
224
231
|
...runSteps(extraScript),
|
|
225
232
|
...(job.jobName === "apply" ? [saveRecord(job.wave)] : []),
|
|
226
233
|
{
|
|
@@ -239,7 +246,7 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
239
246
|
"github.event_name == 'issue_comment'",
|
|
240
247
|
// Forgejo's payload says so with is_pull (terragucci comment-apply.ts:129 at e6cb431).
|
|
241
248
|
forgejo ? "(github.event.issue.pull_request || github.event.is_pull)" : "github.event.issue.pull_request",
|
|
242
|
-
`startsWith(github.event.comment.body, '${
|
|
249
|
+
`startsWith(github.event.comment.body, '${prApplyComment(loop)}')`,
|
|
243
250
|
...(forgejo ? [] : ["contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)"]),
|
|
244
251
|
].join(" && ");
|
|
245
252
|
const applyJob = (job, needs, name) => ({
|
|
@@ -248,7 +255,8 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
248
255
|
"runs-on": GITHUB_RUNNER,
|
|
249
256
|
...(needs.length > 0 ? { needs } : {}),
|
|
250
257
|
container: image,
|
|
251
|
-
|
|
258
|
+
// #4552: on the repo grain every apply of the repository shares one group.
|
|
259
|
+
concurrency: concurrency(loop.lockGrain === "repo" ? REPO_APPLY_GROUP : prApplyGroup(env, member?.name)),
|
|
252
260
|
permissions: withIdToken({ contents: "write", "pull-requests": "write", statuses: "write" }),
|
|
253
261
|
...(job.wave ? { outputs: { [WAVE_GREEN_OUTPUT]: `\${{ steps.${WAVE_STEP_ID}.outputs.${WAVE_GREEN_OUTPUT} }}` } } : {}),
|
|
254
262
|
steps: steps(job, name),
|
|
@@ -281,7 +289,7 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
281
289
|
env: { [CHECKOUT_TOKEN_ENV]: GITHUB_JOB_TOKEN },
|
|
282
290
|
run: checkoutScript(true, true),
|
|
283
291
|
},
|
|
284
|
-
...[...prLoopSetup(image), ...beforeScript].map((line) => headStep(line)),
|
|
292
|
+
...[...prLoopSetup(image, loop.engineSetup), ...beforeScript].map((line) => headStep(line)),
|
|
285
293
|
...(plan.build ? [headStep(plan.build[0], "Build the templates"), headStep(plan.build[1], "Lint the project")] : []),
|
|
286
294
|
// The job's last step: a step after it would start beside whatever the change's code left running.
|
|
287
295
|
{
|
|
@@ -291,14 +299,58 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
291
299
|
run: [drop, ...handoverTrap(PR_LOOP_REPORT_DIR, forgeName), ...(plan.checkout ?? []), ...(plan.setup ?? []), plan.run.command, ...extraScript].join("\n"),
|
|
292
300
|
},
|
|
293
301
|
];
|
|
294
|
-
|
|
302
|
+
// #4568: once the last apply on merge succeeded, one job tells the control repo this member applied.
|
|
303
|
+
const lastGreen = loop.control && !loop.applyBeforeMerge && greenJobs.length > 0 ? greenJobs[greenJobs.length - 1][0] : undefined;
|
|
304
|
+
const controlJobs = [];
|
|
305
|
+
if (loop.control && lastGreen) {
|
|
306
|
+
// A wave job hands on the commit it applied; a single apply job applies the green commit its trigger names.
|
|
307
|
+
const sha = loop.waveGate ? `\${{ needs.${lastGreen}.outputs.${WAVE_GREEN_OUTPUT} }}` : forgejo ? "${{ github.sha }}" : "${{ github.event.workflow_run.head_sha || github.sha }}";
|
|
308
|
+
const dispatch = controlDispatchJob(forgeName, loop.control.target, sha, {
|
|
309
|
+
...(loop.words ? { words: loop.words } : {}),
|
|
310
|
+
...(loop.control.sentCheck ? { sentCheck: loop.control.sentCheck } : {}),
|
|
311
|
+
...(loop.control.markSent ? { markSent: loop.control.markSent } : {}),
|
|
312
|
+
});
|
|
313
|
+
controlJobs.push([
|
|
314
|
+
dispatch.name,
|
|
315
|
+
{
|
|
316
|
+
needs: [lastGreen],
|
|
317
|
+
if: `success() && (${onGreen})`,
|
|
318
|
+
"runs-on": GITHUB_RUNNER,
|
|
319
|
+
container: image,
|
|
320
|
+
permissions: loop.control.markSent ? { contents: "read", statuses: "write" } : { contents: "read" },
|
|
321
|
+
steps: [{ name: dispatch.stepName, ...(rooted ? {} : { "working-directory": "." }), env: { ...dispatch.env, ...(loop.control.sentCheck || loop.control.markSent ? { GITHUB_TOKEN: GITHUB_JOB_TOKEN } : {}) }, run: dispatch.script }],
|
|
322
|
+
},
|
|
323
|
+
]);
|
|
324
|
+
}
|
|
325
|
+
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 } : {}) });
|
|
326
|
+
// #4628: on a push to the target branch, the open pull requests' notes posted against another commit are marked stale.
|
|
327
|
+
const stale = prStaleJob(forgeName, env, loop, member?.name);
|
|
328
|
+
const staleJobs = stale
|
|
329
|
+
? {
|
|
330
|
+
[PR_STALE_JOB]: {
|
|
331
|
+
if: "github.event_name == 'push' && startsWith(github.ref, 'refs/heads/')",
|
|
332
|
+
"runs-on": GITHUB_RUNNER,
|
|
333
|
+
container: image,
|
|
334
|
+
permissions: { contents: "read", "pull-requests": "write", statuses: "write" },
|
|
335
|
+
steps: [
|
|
336
|
+
// The pushed commit: the target branch's, so none of a pull request's code is here.
|
|
337
|
+
{ name: "Check out the target branch's commit", uses: CHECKOUT },
|
|
338
|
+
...runSteps(beforeScript),
|
|
339
|
+
{ name: "Mark the open pull requests' notes stale", ...(rooted ? {} : { "working-directory": "." }), env: stale.env, run: stale.command },
|
|
340
|
+
],
|
|
341
|
+
},
|
|
342
|
+
}
|
|
343
|
+
: {};
|
|
344
|
+
// #4619: with a control repo named, the note job reads its chant/lifecycle, read-only, so pr-post marks the cross-stack edges.
|
|
345
|
+
const controlRead = loop.control ? controlNoteCheckout(forgeName, loop.control.target, loop.words ?? {}) : undefined;
|
|
295
346
|
const noteSteps = [
|
|
296
347
|
// The target branch's commit: the chant this job runs is the target branch's, and none of the change's code is here.
|
|
297
348
|
{ name: "Check out the target branch's commit", uses: CHECKOUT, with: { ref: "${{ github.event.pull_request.base.sha }}" } },
|
|
298
349
|
...runSteps(beforeScript),
|
|
299
350
|
{ name: "Take the plan report from the plan job", run: takeOverScript("plan", PR_LOOP_REPORT_DIR, forgeName) },
|
|
300
351
|
{ name: "Keep the plan report", if: "always()", uses: UPLOAD_ARTIFACT, with: { name: reportName(plan), path: reportPath, "if-no-files-found": "ignore", "include-hidden-files": true } },
|
|
301
|
-
{ name:
|
|
352
|
+
...(controlRead ? [{ name: controlRead.stepName, if: "always()", env: controlRead.env, run: controlRead.script }] : []),
|
|
353
|
+
{ name: `Post the plan note and ${productStatusContext(resolveProductWords(loop.words), "plan")}`, if: "always()", env: post.env, run: post.command },
|
|
302
354
|
];
|
|
303
355
|
// #4298: with a pull request environment the pull_request trigger fires on close too, and the plan skips that event.
|
|
304
356
|
const prEnv = loop.prEnvironment ? prEnvGithubJobs(loop.forge ?? "github", image, beforeScript, extraScript, loop, member) : undefined;
|
|
@@ -339,6 +391,80 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
339
391
|
},
|
|
340
392
|
}
|
|
341
393
|
: {};
|
|
394
|
+
// #4588: `/<product> lock` and `/<product> unlock` comments, at the target branch's commit, so none of the change's code runs.
|
|
395
|
+
const commentLocks = prCommentLockJobs(forgeName, loop);
|
|
396
|
+
const commentLockJobs = Object.fromEntries(commentLocks.map((job) => [
|
|
397
|
+
job.jobName,
|
|
398
|
+
{
|
|
399
|
+
if: [
|
|
400
|
+
"github.event_name == 'issue_comment'",
|
|
401
|
+
forgejo ? "(github.event.issue.pull_request || github.event.is_pull)" : "github.event.issue.pull_request",
|
|
402
|
+
`startsWith(github.event.comment.body, '${job.comment}')`,
|
|
403
|
+
...(forgejo ? [] : ["contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)"]),
|
|
404
|
+
].join(" && "),
|
|
405
|
+
"runs-on": GITHUB_RUNNER,
|
|
406
|
+
container: image,
|
|
407
|
+
permissions: { contents: "write", "pull-requests": "read", statuses: "write" },
|
|
408
|
+
steps: [
|
|
409
|
+
{ name: "Check out the target branch's commit", uses: CHECKOUT, with: { "fetch-depth": 0 } },
|
|
410
|
+
...runSteps(beforeScript),
|
|
411
|
+
{ name: job.command === "lock" ? "Keep the locks the pull request's plan took" : "Release the pull request's locks", env: job.env, run: job.line },
|
|
412
|
+
],
|
|
413
|
+
},
|
|
414
|
+
]));
|
|
415
|
+
// #4620: `/<product> <plan word>` plans the pull request's head again with a read token; the job after it posts the note.
|
|
416
|
+
const preview = prCommentPreviewJob(forgeName, env, loop, member?.name);
|
|
417
|
+
const previewJobs = preview
|
|
418
|
+
? {
|
|
419
|
+
[preview.jobName]: {
|
|
420
|
+
if: [
|
|
421
|
+
"github.event_name == 'issue_comment'",
|
|
422
|
+
forgejo ? "(github.event.issue.pull_request || github.event.is_pull)" : "github.event.issue.pull_request",
|
|
423
|
+
`startsWith(github.event.comment.body, '${preview.comment}')`,
|
|
424
|
+
...(forgejo ? [] : ["contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)"]),
|
|
425
|
+
].join(" && "),
|
|
426
|
+
"runs-on": GITHUB_RUNNER,
|
|
427
|
+
container: image,
|
|
428
|
+
permissions: withIdToken({ contents: "read" }),
|
|
429
|
+
outputs: { ...handoverOutputs(PLAN_STEP_ID, forgeName), [PREVIEW_HEAD_OUTPUT]: `\${{ steps.${PREVIEW_HEAD_STEP_ID}.outputs.${PREVIEW_HEAD_OUTPUT} }}` },
|
|
430
|
+
steps: [
|
|
431
|
+
...(identity ? [tradeStep(identity.plan, "plan")] : []),
|
|
432
|
+
// The head is read here, before any of the change's code runs, so the post job names the commit this job planned.
|
|
433
|
+
{
|
|
434
|
+
name: "Check out the pull request's head",
|
|
435
|
+
id: PREVIEW_HEAD_STEP_ID,
|
|
436
|
+
...(rooted ? {} : { "working-directory": "." }),
|
|
437
|
+
env: { [CHECKOUT_TOKEN_ENV]: GITHUB_JOB_TOKEN, [PREVIEW_REF_ENV]: "ref" in preview.head ? preview.head.ref : "" },
|
|
438
|
+
run: [checkoutScript(true, true, { refEnv: PREVIEW_REF_ENV }), `echo "${PREVIEW_HEAD_OUTPUT}=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"`].join("\n"),
|
|
439
|
+
},
|
|
440
|
+
...[...prLoopSetup(image, loop.engineSetup), ...beforeScript].map((line) => headStep(line)),
|
|
441
|
+
...(preview.plan.build ? [headStep(preview.plan.build[0], "Build the templates"), headStep(preview.plan.build[1], "Lint the project")] : []),
|
|
442
|
+
{
|
|
443
|
+
name: "Preview the members this pull request reaches",
|
|
444
|
+
id: PLAN_STEP_ID,
|
|
445
|
+
env: preview.plan.run.env,
|
|
446
|
+
run: [drop, ...handoverTrap(PR_LOOP_REPORT_DIR, forgeName), preview.base, ...(preview.plan.setup ?? []), preview.plan.run.command, ...extraScript].join("\n"),
|
|
447
|
+
},
|
|
448
|
+
],
|
|
449
|
+
},
|
|
450
|
+
[preview.noteJobName]: {
|
|
451
|
+
needs: [preview.jobName],
|
|
452
|
+
if: `always() && github.event_name == 'issue_comment' && (needs.${preview.jobName}.result == 'success' || needs.${preview.jobName}.result == 'failure')`,
|
|
453
|
+
"runs-on": GITHUB_RUNNER,
|
|
454
|
+
container: image,
|
|
455
|
+
permissions: { actions: "read", contents: "read", "pull-requests": "write", statuses: "write" },
|
|
456
|
+
steps: [
|
|
457
|
+
// The target branch's commit, which an issue_comment run checks out: none of the change's code is here.
|
|
458
|
+
{ name: "Check out the target branch's commit", uses: CHECKOUT },
|
|
459
|
+
...runSteps(beforeScript),
|
|
460
|
+
{ name: "Take the preview report from the preview job", run: takeOverScript(preview.jobName, PR_LOOP_REPORT_DIR, forgeName) },
|
|
461
|
+
{ name: "Keep the preview report", if: "always()", uses: UPLOAD_ARTIFACT, with: { name: `chant-pr-${preview.jobName}`, path: reportPath, "if-no-files-found": "ignore", "include-hidden-files": true } },
|
|
462
|
+
...(controlRead ? [{ name: controlRead.stepName, if: "always()", env: controlRead.env, run: controlRead.script }] : []),
|
|
463
|
+
{ name: `Post the preview note and ${productStatusContext(resolveProductWords(loop.words), "plan")}`, if: "always()", env: preview.post.env, run: preview.post.command },
|
|
464
|
+
],
|
|
465
|
+
},
|
|
466
|
+
}
|
|
467
|
+
: {};
|
|
342
468
|
const jobsDoc = {
|
|
343
469
|
plan: {
|
|
344
470
|
if: closes ? "github.event_name == 'pull_request' && github.event.action != 'closed'" : "github.event_name == 'pull_request'",
|
|
@@ -358,7 +484,11 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
358
484
|
steps: noteSteps,
|
|
359
485
|
},
|
|
360
486
|
...lockJobs,
|
|
487
|
+
...commentLockJobs,
|
|
488
|
+
...previewJobs,
|
|
489
|
+
...staleJobs,
|
|
361
490
|
...Object.fromEntries(applyJobs),
|
|
491
|
+
...Object.fromEntries(controlJobs),
|
|
362
492
|
...(prEnv ? prEnv.jobs : {}),
|
|
363
493
|
};
|
|
364
494
|
const applyNames = applyJobs.map(([name]) => name);
|
|
@@ -373,9 +503,15 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
373
503
|
on.pull_request = { branches: [branch], types: ["opened", "synchronize", "reopened", "closed"] };
|
|
374
504
|
on.schedule = [{ cron: PR_ENV_SWEEP_CRON }];
|
|
375
505
|
}
|
|
376
|
-
// #4301: a /chant apply comment starts the apply before merge.
|
|
377
|
-
if (loop.applyBeforeMerge)
|
|
506
|
+
// #4301: a /chant apply comment starts the apply before merge. #4588: and a lock or unlock comment its job.
|
|
507
|
+
if (loop.applyBeforeMerge || commentLocks.length > 0 || preview)
|
|
378
508
|
on.issue_comment = { types: ["created"] };
|
|
509
|
+
// #4625: each job on its stage's runner, the plan side's, the apply side's, or the default.
|
|
510
|
+
if (loop.runners) {
|
|
511
|
+
for (const [name, job] of Object.entries(jobsDoc)) {
|
|
512
|
+
job["runs-on"] = githubRunsOn(prLoopRunner(loop, prLoopJobStage(name)));
|
|
513
|
+
}
|
|
514
|
+
}
|
|
379
515
|
return {
|
|
380
516
|
name: member ? `chant-pr.${member.name}.${env}` : `chant-pr-${env}`,
|
|
381
517
|
environment: env,
|
|
@@ -383,16 +519,24 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
383
519
|
...(rooted ? {} : { defaults: { run: { "working-directory": member.dir } } }),
|
|
384
520
|
env: { ...options.variables, CHANT_ENV: env },
|
|
385
521
|
jobsDoc,
|
|
386
|
-
stages: ["plan", PR_PLAN_NOTE_JOB, ...(locks ? [PR_LOCK_JOB, PR_UNLOCK_JOB] : []), ...applyNames, ...(prEnv ? ["env-up", PR_ENV_POST_JOB, "env-down", "env-sweep"] : [])],
|
|
522
|
+
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"] : [])],
|
|
387
523
|
jobs: [
|
|
388
524
|
{ jobName: "plan", component: "pull request plan", stage: "plan", needs: [] },
|
|
389
525
|
{ jobName: PR_PLAN_NOTE_JOB, component: "pull request plan note", stage: PR_PLAN_NOTE_JOB, needs: ["plan"] },
|
|
526
|
+
...(stale ? [{ jobName: PR_STALE_JOB, component: "pull request stale notes", stage: PR_STALE_JOB, needs: [] }] : []),
|
|
390
527
|
...(locks
|
|
391
528
|
? [
|
|
392
529
|
{ jobName: PR_LOCK_JOB, component: "pull request lock", stage: PR_LOCK_JOB, needs: ["plan"] },
|
|
393
530
|
{ jobName: PR_UNLOCK_JOB, component: "pull request unlock", stage: PR_UNLOCK_JOB, needs: [] },
|
|
394
531
|
]
|
|
395
532
|
: []),
|
|
533
|
+
...commentLocks.map((j) => ({ jobName: j.jobName, component: `pull request ${j.command} comment`, stage: j.jobName, needs: [] })),
|
|
534
|
+
...(preview
|
|
535
|
+
? [
|
|
536
|
+
{ jobName: preview.jobName, component: "pull request preview comment", stage: preview.jobName, needs: [] },
|
|
537
|
+
{ jobName: preview.noteJobName, component: "pull request preview comment note", stage: preview.noteJobName, needs: [preview.jobName] },
|
|
538
|
+
]
|
|
539
|
+
: []),
|
|
396
540
|
...applyNames.map((name, i) => ({
|
|
397
541
|
jobName: name,
|
|
398
542
|
component: name === "record" ? "merge record" : loop.applyBeforeMerge ? "apply before merge" : loop.waveGate ? `green apply, wave ${i}` : "green apply",
|
|
@@ -400,6 +544,7 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
400
544
|
// The record runs on a push and the apply on a green commit, so only the waves need each other.
|
|
401
545
|
needs: i > 1 ? [applyNames[i - 1]] : [],
|
|
402
546
|
})),
|
|
547
|
+
...controlJobs.map(([name]) => ({ jobName: name, component: "control repo dispatch", stage: name, needs: [lastGreen] })),
|
|
403
548
|
...(prEnv ? prEnv.listed : []),
|
|
404
549
|
],
|
|
405
550
|
};
|
|
@@ -451,7 +596,7 @@ function prEnvGithubJobs(forge, image, beforeScript, extraScript, loop, member)
|
|
|
451
596
|
env: { [CHECKOUT_TOKEN_ENV]: GITHUB_JOB_TOKEN },
|
|
452
597
|
run: checkoutScript(true, true),
|
|
453
598
|
},
|
|
454
|
-
...[...prLoopSetup(image), ...beforeScript].map((line) => headStep(line)),
|
|
599
|
+
...[...prLoopSetup(image, loop.engineSetup), ...beforeScript].map((line) => headStep(line)),
|
|
455
600
|
...(up.build ? [headStep(up.build[0], "Build the templates", { CHANT_ENV: up.env.CHANT_ENV }), headStep(up.build[1], "Lint the project")] : []),
|
|
456
601
|
// The job's last step: a step after it would start beside whatever the change's code left running.
|
|
457
602
|
{
|
|
@@ -487,7 +632,7 @@ function prEnvGithubJobs(forge, image, beforeScript, extraScript, loop, member)
|
|
|
487
632
|
steps: [
|
|
488
633
|
...tradeStep,
|
|
489
634
|
{ uses: CHECKOUT, with: { "fetch-depth": 0, ...(ref ? { ref } : {}) } },
|
|
490
|
-
...runSteps(prLoopSetup(image)),
|
|
635
|
+
...runSteps(prLoopSetup(image, loop.engineSetup)),
|
|
491
636
|
...runSteps(beforeScript),
|
|
492
637
|
{ name, env: j.env, run: j.command },
|
|
493
638
|
...runSteps(extraScript),
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "16478bfa3a3a6b6a573c54713b9d6edcef575a934df2b6a2daba331607d0d132",
|
|
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": "9eb8c4e72039a3e7c014959fe55b6846823a49f4097e41c3d4b2b9baebe12ad4"
|
|
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.56",
|
|
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.56"
|
|
65
65
|
}
|
|
66
66
|
}
|