willfire 0.1.13 → 0.1.15
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/cli/parseArgs.js +2 -1
- package/dist/cli.js +4 -2
- package/dist/execute.js +76 -38
- package/dist/filters/matchFilters.js +2 -1
- package/dist/jobs/evalIf.js +4 -2
- package/dist/jobs/expandJobs.js +16 -8
- package/dist/verify.js +2 -1
- package/package.json +1 -1
package/dist/cli/parseArgs.js
CHANGED
|
@@ -27,8 +27,9 @@ export function parseArgs(argv) {
|
|
|
27
27
|
// execution the caller thought they asked for.
|
|
28
28
|
const execute = [];
|
|
29
29
|
for (let i = 0; i < argv.length; i++) {
|
|
30
|
-
if (argv[i] !== "--execute")
|
|
30
|
+
if (argv[i] !== "--execute") {
|
|
31
31
|
continue;
|
|
32
|
+
}
|
|
32
33
|
const spec = argv[i + 1];
|
|
33
34
|
const grant = spec == null ? null : parseGrant(spec);
|
|
34
35
|
if (grant == null) {
|
package/dist/cli.js
CHANGED
|
@@ -25,8 +25,9 @@ if (isMain) {
|
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
27
27
|
for (const e of entries) {
|
|
28
|
-
if (isWorkflowEntry(e))
|
|
28
|
+
if (isWorkflowEntry(e)) {
|
|
29
29
|
console.log(`# ${e.workflow} :: ${e.status} (${e.reason})`);
|
|
30
|
+
}
|
|
30
31
|
else {
|
|
31
32
|
const name = e.checkName ?? `${e.job} (name unresolved)`;
|
|
32
33
|
console.log(`${e.workflow} :: ${name} :: ${e.status}`);
|
|
@@ -35,7 +36,8 @@ if (isMain) {
|
|
|
35
36
|
}
|
|
36
37
|
// Last, and on the skip path too, so a red gate's first question — which
|
|
37
38
|
// commits was this read from? — is answered wherever the reader lands.
|
|
38
|
-
for (const s of sources)
|
|
39
|
+
for (const s of sources) {
|
|
39
40
|
console.log(`# read ${s.owner}/${s.repo}@${s.ref} -> ${s.sha}`);
|
|
41
|
+
}
|
|
40
42
|
}
|
|
41
43
|
}
|
package/dist/execute.js
CHANGED
|
@@ -40,19 +40,22 @@ import { UNKNOWN } from "./expr/val.js";
|
|
|
40
40
|
/** `owner/repo:job1,job2` as the CLI spells a grant. */
|
|
41
41
|
export function parseGrant(spec) {
|
|
42
42
|
const colon = spec.indexOf(":");
|
|
43
|
-
if (colon <= 0)
|
|
43
|
+
if (colon <= 0) {
|
|
44
44
|
return null;
|
|
45
|
+
}
|
|
45
46
|
const repo = spec.slice(0, colon);
|
|
46
47
|
const parts = repo.split("/");
|
|
47
|
-
if (parts.length !== 2 || parts.some((p) => p === ""))
|
|
48
|
+
if (parts.length !== 2 || parts.some((p) => p === "")) {
|
|
48
49
|
return null;
|
|
50
|
+
}
|
|
49
51
|
const jobs = spec
|
|
50
52
|
.slice(colon + 1)
|
|
51
53
|
.split(",")
|
|
52
54
|
.map((s) => s.trim())
|
|
53
55
|
.filter((s) => s !== "");
|
|
54
|
-
if (jobs.length === 0)
|
|
56
|
+
if (jobs.length === 0) {
|
|
55
57
|
return null;
|
|
58
|
+
}
|
|
56
59
|
return { repo, jobs };
|
|
57
60
|
}
|
|
58
61
|
const err = (reason) => ({ ok: false, reason });
|
|
@@ -77,15 +80,18 @@ function renderTemplate(text, scope) {
|
|
|
77
80
|
}
|
|
78
81
|
/** An `env:` block rendered to concrete strings, every key or nothing. */
|
|
79
82
|
function renderEnvLayer(layer, scope) {
|
|
80
|
-
if (layer == null)
|
|
83
|
+
if (layer == null) {
|
|
81
84
|
return { ok: true, v: {} };
|
|
82
|
-
|
|
85
|
+
}
|
|
86
|
+
if (typeof layer !== "object" || Array.isArray(layer)) {
|
|
83
87
|
return err("env block is not a map");
|
|
88
|
+
}
|
|
84
89
|
const out = {};
|
|
85
90
|
for (const [k, raw] of Object.entries(layer)) {
|
|
86
91
|
const rendered = renderTemplate(String(raw ?? ""), scope);
|
|
87
|
-
if (rendered == null)
|
|
92
|
+
if (rendered == null) {
|
|
88
93
|
return err(`cannot resolve env '${k}'`);
|
|
94
|
+
}
|
|
89
95
|
out[k] = rendered;
|
|
90
96
|
}
|
|
91
97
|
return { ok: true, v: out };
|
|
@@ -103,15 +109,17 @@ export function parseGithubOutput(text) {
|
|
|
103
109
|
while (i < lines.length) {
|
|
104
110
|
const line = lines[i];
|
|
105
111
|
i++;
|
|
106
|
-
if (line === "")
|
|
112
|
+
if (line === "") {
|
|
107
113
|
continue;
|
|
114
|
+
}
|
|
108
115
|
const heredoc = /^([^=<]+)<<(.+)$/.exec(line);
|
|
109
116
|
if (heredoc != null) {
|
|
110
117
|
const [, name, delim] = heredoc;
|
|
111
118
|
const buf = [];
|
|
112
119
|
for (;;) {
|
|
113
|
-
if (i >= lines.length)
|
|
120
|
+
if (i >= lines.length) {
|
|
114
121
|
return null; // unterminated heredoc
|
|
122
|
+
}
|
|
115
123
|
if (lines[i] === delim) {
|
|
116
124
|
i++;
|
|
117
125
|
break;
|
|
@@ -123,8 +131,9 @@ export function parseGithubOutput(text) {
|
|
|
123
131
|
continue;
|
|
124
132
|
}
|
|
125
133
|
const eq = line.indexOf("=");
|
|
126
|
-
if (eq <= 0)
|
|
134
|
+
if (eq <= 0) {
|
|
127
135
|
return null;
|
|
136
|
+
}
|
|
128
137
|
out[line.slice(0, eq)] = line.slice(eq + 1);
|
|
129
138
|
}
|
|
130
139
|
return out;
|
|
@@ -136,17 +145,21 @@ export function parseGithubOutput(text) {
|
|
|
136
145
|
* lives at the repo root. Expressions and `docker://` images return null.
|
|
137
146
|
*/
|
|
138
147
|
function parseActionUses(uses) {
|
|
139
|
-
if (uses.includes("${{") || uses.startsWith("docker://"))
|
|
148
|
+
if (uses.includes("${{") || uses.startsWith("docker://")) {
|
|
140
149
|
return null;
|
|
150
|
+
}
|
|
141
151
|
const at = uses.lastIndexOf("@");
|
|
142
|
-
if (at <= 0)
|
|
152
|
+
if (at <= 0) {
|
|
143
153
|
return null;
|
|
154
|
+
}
|
|
144
155
|
const ref = uses.slice(at + 1);
|
|
145
|
-
if (ref === "")
|
|
156
|
+
if (ref === "") {
|
|
146
157
|
return null;
|
|
158
|
+
}
|
|
147
159
|
const [owner, repo, ...rest] = uses.slice(0, at).split("/");
|
|
148
|
-
if (!owner || !repo)
|
|
160
|
+
if (!owner || !repo) {
|
|
149
161
|
return null;
|
|
162
|
+
}
|
|
150
163
|
return { path: rest.join("/"), source: { owner, repo, ref } };
|
|
151
164
|
}
|
|
152
165
|
/** Read `action.yml` (or `.yaml`) from a directory, or null if neither exists. */
|
|
@@ -171,8 +184,9 @@ async function readActionManifest(dir) {
|
|
|
171
184
|
*/
|
|
172
185
|
function bindActionInputs(action, withBlock, scope) {
|
|
173
186
|
const bind = (raw) => {
|
|
174
|
-
if (raw == null)
|
|
187
|
+
if (raw == null) {
|
|
175
188
|
return { kind: "value", v: "" };
|
|
189
|
+
}
|
|
176
190
|
if (typeof raw === "boolean" || typeof raw === "number") {
|
|
177
191
|
return { kind: "value", v: String(raw) };
|
|
178
192
|
}
|
|
@@ -213,13 +227,15 @@ async function runSteps(steps, scope, ctx) {
|
|
|
213
227
|
const stepScope = { ...scope, steps: stepsCtx };
|
|
214
228
|
if (step.if != null) {
|
|
215
229
|
const verdict = evaluate(String(step.if), stepScope);
|
|
216
|
-
if (verdict == null)
|
|
230
|
+
if (verdict == null) {
|
|
217
231
|
return err(`cannot decide if: for ${label}`);
|
|
232
|
+
}
|
|
218
233
|
if (!verdict) {
|
|
219
234
|
// A skipped step still occupies its id, with no outputs — that is the
|
|
220
235
|
// empty string every later read gets, and what `||` coalesces past.
|
|
221
|
-
if (typeof step.id === "string")
|
|
236
|
+
if (typeof step.id === "string") {
|
|
222
237
|
stepsCtx[step.id] = { outputs: {} };
|
|
238
|
+
}
|
|
223
239
|
continue;
|
|
224
240
|
}
|
|
225
241
|
}
|
|
@@ -233,10 +249,12 @@ async function runSteps(steps, scope, ctx) {
|
|
|
233
249
|
else {
|
|
234
250
|
return err(`${label} has neither uses nor run`);
|
|
235
251
|
}
|
|
236
|
-
if (!res.ok)
|
|
252
|
+
if (!res.ok) {
|
|
237
253
|
return res;
|
|
238
|
-
|
|
254
|
+
}
|
|
255
|
+
if (typeof step.id === "string") {
|
|
239
256
|
stepsCtx[step.id] = { outputs: res.v };
|
|
257
|
+
}
|
|
240
258
|
}
|
|
241
259
|
return { ok: true, v: stepsCtx };
|
|
242
260
|
}
|
|
@@ -263,12 +281,14 @@ async function runUses(step, label, scope, ctx) {
|
|
|
263
281
|
}
|
|
264
282
|
else {
|
|
265
283
|
const target = parseActionUses(uses);
|
|
266
|
-
if (target == null)
|
|
284
|
+
if (target == null) {
|
|
267
285
|
return err(`${label}: unresolvable uses: ${uses}`);
|
|
286
|
+
}
|
|
268
287
|
const { ref } = target.source;
|
|
269
288
|
const sha = SHA_RE.test(ref) ? ref : await ctx.deps.resolveRef(target.source);
|
|
270
|
-
if (sha == null)
|
|
289
|
+
if (sha == null) {
|
|
271
290
|
return err(`${label}: cannot resolve ref for ${uses}`);
|
|
291
|
+
}
|
|
272
292
|
const source = { ...target.source, sha };
|
|
273
293
|
const root = await ctx.deps.provideTree(source);
|
|
274
294
|
if (root == null) {
|
|
@@ -277,8 +297,9 @@ async function runUses(step, label, scope, ctx) {
|
|
|
277
297
|
actionDir = target.path === "" ? root : join(root, target.path);
|
|
278
298
|
}
|
|
279
299
|
const manifest = await readActionManifest(actionDir);
|
|
280
|
-
if (manifest == null)
|
|
300
|
+
if (manifest == null) {
|
|
281
301
|
return err(`${label}: no action.yml under ${uses}`);
|
|
302
|
+
}
|
|
282
303
|
let action;
|
|
283
304
|
try {
|
|
284
305
|
action = parseYaml(manifest);
|
|
@@ -302,19 +323,22 @@ async function runUses(step, label, scope, ctx) {
|
|
|
302
323
|
actionPath: actionDir,
|
|
303
324
|
depth: ctx.depth + 1,
|
|
304
325
|
});
|
|
305
|
-
if (!walked.ok)
|
|
326
|
+
if (!walked.ok) {
|
|
306
327
|
return err(`${label} (${uses}): ${walked.reason}`);
|
|
328
|
+
}
|
|
307
329
|
// The action's declared outputs are its whole surface: each `value:` is
|
|
308
330
|
// evaluated against the child's own steps, and every one must land.
|
|
309
331
|
const outScope = { ...childScope, steps: walked.v };
|
|
310
332
|
const outputs = {};
|
|
311
333
|
for (const [name, decl] of Object.entries(action.outputs ?? {})) {
|
|
312
334
|
const raw = decl?.["value"];
|
|
313
|
-
if (raw == null)
|
|
335
|
+
if (raw == null) {
|
|
314
336
|
return err(`${label}: output '${name}' of ${uses} has no value`);
|
|
337
|
+
}
|
|
315
338
|
const rendered = renderTemplate(String(raw), outScope);
|
|
316
|
-
if (rendered == null)
|
|
339
|
+
if (rendered == null) {
|
|
317
340
|
return err(`${label}: cannot resolve output '${name}' of ${uses}`);
|
|
341
|
+
}
|
|
318
342
|
outputs[name] = rendered;
|
|
319
343
|
}
|
|
320
344
|
return { ok: true, v: outputs };
|
|
@@ -326,8 +350,9 @@ async function runRun(step, label, scope, ctx) {
|
|
|
326
350
|
return err(`${label}: shell '${shell}' is not modelled`);
|
|
327
351
|
}
|
|
328
352
|
const script = renderTemplate(String(step.run), scope);
|
|
329
|
-
if (script == null)
|
|
353
|
+
if (script == null) {
|
|
330
354
|
return err(`${label}: cannot resolve \${{ }} in run`);
|
|
355
|
+
}
|
|
331
356
|
const env = {
|
|
332
357
|
// The two the runner always provides and scripts assume. Everything else
|
|
333
358
|
// a step sees, it declared.
|
|
@@ -335,19 +360,22 @@ async function runRun(step, label, scope, ctx) {
|
|
|
335
360
|
HOME: process.env.HOME ?? "",
|
|
336
361
|
GITHUB_WORKSPACE: ctx.tree,
|
|
337
362
|
};
|
|
338
|
-
if (ctx.actionPath != null)
|
|
363
|
+
if (ctx.actionPath != null) {
|
|
339
364
|
env.GITHUB_ACTION_PATH = ctx.actionPath;
|
|
365
|
+
}
|
|
340
366
|
for (const layer of [...ctx.envLayers, step.env]) {
|
|
341
367
|
const rendered = renderEnvLayer(layer, scope);
|
|
342
|
-
if (!rendered.ok)
|
|
368
|
+
if (!rendered.ok) {
|
|
343
369
|
return err(`${label}: ${rendered.reason}`);
|
|
370
|
+
}
|
|
344
371
|
Object.assign(env, rendered.v);
|
|
345
372
|
}
|
|
346
373
|
let cwd = ctx.tree;
|
|
347
374
|
if (step["working-directory"] != null) {
|
|
348
375
|
const wd = renderTemplate(String(step["working-directory"]), scope);
|
|
349
|
-
if (wd == null)
|
|
376
|
+
if (wd == null) {
|
|
350
377
|
return err(`${label}: cannot resolve working-directory`);
|
|
378
|
+
}
|
|
351
379
|
cwd = resolve(ctx.tree, wd);
|
|
352
380
|
}
|
|
353
381
|
const outDir = await mkdtemp(join(tmpdir(), "willfire-out-"));
|
|
@@ -362,8 +390,9 @@ async function runRun(step, label, scope, ctx) {
|
|
|
362
390
|
return err(`${label}: exited ${r.code}${tail === "" ? "" : ` (${tail})`}`);
|
|
363
391
|
}
|
|
364
392
|
const outputs = parseGithubOutput(await readFile(outFile, "utf8"));
|
|
365
|
-
if (outputs == null)
|
|
393
|
+
if (outputs == null) {
|
|
366
394
|
return err(`${label}: malformed GITHUB_OUTPUT`);
|
|
395
|
+
}
|
|
367
396
|
return { ok: true, v: outputs };
|
|
368
397
|
}
|
|
369
398
|
// ------------------------------------------------------------------ executor
|
|
@@ -382,13 +411,15 @@ export function makeExecutor(opts) {
|
|
|
382
411
|
// The shapes execution does not model, refused by name rather than run
|
|
383
412
|
// wrong: a matrix'd job is several executions, and a container changes
|
|
384
413
|
// what every step means.
|
|
385
|
-
if (job.strategy != null)
|
|
414
|
+
if (job.strategy != null) {
|
|
386
415
|
return fail(`job '${jobId}' has a strategy; not modelled`);
|
|
416
|
+
}
|
|
387
417
|
if (job.container != null || job.services != null) {
|
|
388
418
|
return fail(`job '${jobId}' uses a container or services; not modelled`);
|
|
389
419
|
}
|
|
390
|
-
if (!Array.isArray(job.steps))
|
|
420
|
+
if (!Array.isArray(job.steps)) {
|
|
391
421
|
return fail(`job '${jobId}' has no steps`);
|
|
422
|
+
}
|
|
392
423
|
const tree = await deps.provideTree(workspace);
|
|
393
424
|
if (tree == null) {
|
|
394
425
|
return fail(`cannot materialize workspace ${workspace.owner}/${workspace.repo}@${workspace.sha}`);
|
|
@@ -400,8 +431,9 @@ export function makeExecutor(opts) {
|
|
|
400
431
|
deps,
|
|
401
432
|
depth: 0,
|
|
402
433
|
});
|
|
403
|
-
if (!walked.ok)
|
|
434
|
+
if (!walked.ok) {
|
|
404
435
|
return fail(walked.reason);
|
|
436
|
+
}
|
|
405
437
|
// The job's `outputs:` map is the whole point of having run anything.
|
|
406
438
|
// Every declared entry must land; a hole here would hand consumers a
|
|
407
439
|
// partial map, which the Scope contract calls a lie.
|
|
@@ -409,8 +441,9 @@ export function makeExecutor(opts) {
|
|
|
409
441
|
const outputs = {};
|
|
410
442
|
for (const [name, raw] of Object.entries(job.outputs ?? {})) {
|
|
411
443
|
const rendered = renderTemplate(String(raw), outScope);
|
|
412
|
-
if (rendered == null)
|
|
444
|
+
if (rendered == null) {
|
|
413
445
|
return fail(`cannot resolve job output '${name}'`);
|
|
446
|
+
}
|
|
414
447
|
outputs[name] = rendered;
|
|
415
448
|
}
|
|
416
449
|
return { ok: true, outputs };
|
|
@@ -436,8 +469,9 @@ export const runShell = (spec) => new Promise((resolvePromise) => {
|
|
|
436
469
|
child.stderr.on("data", (d) => {
|
|
437
470
|
stderr += String(d);
|
|
438
471
|
// Keep the tail; a failure reason wants the last line, not a transcript.
|
|
439
|
-
if (stderr.length > 4096)
|
|
472
|
+
if (stderr.length > 4096) {
|
|
440
473
|
stderr = stderr.slice(-4096);
|
|
474
|
+
}
|
|
441
475
|
});
|
|
442
476
|
child.on("error", () => resolvePromise({ code: 127, stderr }));
|
|
443
477
|
child.on("close", (code) => resolvePromise({ code: code ?? 1, stderr }));
|
|
@@ -454,8 +488,9 @@ export function makeTreeProvider(download, runCommand) {
|
|
|
454
488
|
return (source) => {
|
|
455
489
|
const key = `${source.owner}/${source.repo}@${source.sha}`;
|
|
456
490
|
const hit = cache.get(key);
|
|
457
|
-
if (hit !== undefined)
|
|
491
|
+
if (hit !== undefined) {
|
|
458
492
|
return hit;
|
|
493
|
+
}
|
|
459
494
|
const p = materialize(source, download, runCommand);
|
|
460
495
|
cache.set(key, p);
|
|
461
496
|
return p;
|
|
@@ -463,8 +498,9 @@ export function makeTreeProvider(download, runCommand) {
|
|
|
463
498
|
}
|
|
464
499
|
async function materialize(source, download, runCommand) {
|
|
465
500
|
const bytes = await download(source);
|
|
466
|
-
if (bytes == null)
|
|
501
|
+
if (bytes == null) {
|
|
467
502
|
return null;
|
|
503
|
+
}
|
|
468
504
|
const dir = await mkdtemp(join(tmpdir(), "willfire-tree-"));
|
|
469
505
|
const archive = join(dir, "tree.tar.gz");
|
|
470
506
|
await writeFile(archive, bytes);
|
|
@@ -480,13 +516,15 @@ async function materialize(source, download, runCommand) {
|
|
|
480
516
|
WILLFIRE_DEST: dest,
|
|
481
517
|
},
|
|
482
518
|
});
|
|
483
|
-
if (r.code !== 0)
|
|
519
|
+
if (r.code !== 0) {
|
|
484
520
|
return null;
|
|
521
|
+
}
|
|
485
522
|
const entries = await readdir(dest);
|
|
486
523
|
if (entries.length === 1) {
|
|
487
524
|
const sub = join(dest, entries[0]);
|
|
488
|
-
if ((await stat(sub)).isDirectory())
|
|
525
|
+
if ((await stat(sub)).isDirectory()) {
|
|
489
526
|
return sub;
|
|
527
|
+
}
|
|
490
528
|
}
|
|
491
529
|
return dest;
|
|
492
530
|
}
|
|
@@ -5,8 +5,9 @@ export function matchFilters(value, patterns) {
|
|
|
5
5
|
for (const pat of patterns) {
|
|
6
6
|
const neg = pat.startsWith("!");
|
|
7
7
|
const p = neg ? pat.slice(1) : pat;
|
|
8
|
-
if (patternToRegex(p).test(value))
|
|
8
|
+
if (patternToRegex(p).test(value)) {
|
|
9
9
|
matched = !neg;
|
|
10
|
+
}
|
|
10
11
|
}
|
|
11
12
|
return matched;
|
|
12
13
|
}
|
package/dist/jobs/evalIf.js
CHANGED
|
@@ -9,10 +9,12 @@ import { prScope } from "./prScope.js";
|
|
|
9
9
|
* `needs.*`.
|
|
10
10
|
*/
|
|
11
11
|
export function evalIf(cond, scope = {}) {
|
|
12
|
-
if (cond == null)
|
|
12
|
+
if (cond == null) {
|
|
13
13
|
return "run";
|
|
14
|
+
}
|
|
14
15
|
const verdict = evaluate(String(cond), prScope(scope));
|
|
15
|
-
if (verdict === null)
|
|
16
|
+
if (verdict === null) {
|
|
16
17
|
return "unknown";
|
|
18
|
+
}
|
|
17
19
|
return verdict ? "run" : "skipped";
|
|
18
20
|
}
|
package/dist/jobs/expandJobs.js
CHANGED
|
@@ -16,22 +16,27 @@ import { prScope } from "./prScope.js";
|
|
|
16
16
|
* existed; nothing regresses.
|
|
17
17
|
*/
|
|
18
18
|
function inputLiteral(raw) {
|
|
19
|
-
if (raw == null)
|
|
19
|
+
if (raw == null) {
|
|
20
20
|
return { kind: "value", v: "" };
|
|
21
|
-
|
|
21
|
+
}
|
|
22
|
+
if (typeof raw === "boolean" || typeof raw === "number") {
|
|
22
23
|
return { kind: "value", v: raw };
|
|
23
|
-
|
|
24
|
+
}
|
|
25
|
+
if (typeof raw === "string") {
|
|
24
26
|
return raw.includes("${{") ? UNKNOWN : { kind: "value", v: raw };
|
|
27
|
+
}
|
|
25
28
|
return UNKNOWN;
|
|
26
29
|
}
|
|
27
30
|
/** The `on.workflow_call.inputs` block, tolerating the YAML 1.1 `on` -> true key. */
|
|
28
31
|
function workflowCallInputs(wf) {
|
|
29
32
|
const on = wf?.["on"] ?? wf?.["true"];
|
|
30
|
-
if (on == null || typeof on !== "object")
|
|
33
|
+
if (on == null || typeof on !== "object") {
|
|
31
34
|
return {};
|
|
35
|
+
}
|
|
32
36
|
const call = on["workflow_call"];
|
|
33
|
-
if (call == null || typeof call !== "object")
|
|
37
|
+
if (call == null || typeof call !== "object") {
|
|
34
38
|
return {};
|
|
39
|
+
}
|
|
35
40
|
const inputs = call["inputs"];
|
|
36
41
|
return inputs != null && typeof inputs === "object" ? inputs : {};
|
|
37
42
|
}
|
|
@@ -87,11 +92,13 @@ export async function expandJobs(wf, ctx, reader, source, depth = 0, prefix = ""
|
|
|
87
92
|
const execFailures = {};
|
|
88
93
|
if (executor != null) {
|
|
89
94
|
for (const [jobId, jobRaw] of Object.entries(jobs)) {
|
|
90
|
-
if (!executor.granted(source, jobId))
|
|
95
|
+
if (!executor.granted(source, jobId)) {
|
|
91
96
|
continue;
|
|
97
|
+
}
|
|
92
98
|
const job = jobRaw ?? {};
|
|
93
|
-
if (evalIf(job.if, scoped) !== "run")
|
|
99
|
+
if (evalIf(job.if, scoped) !== "run") {
|
|
94
100
|
continue;
|
|
101
|
+
}
|
|
95
102
|
const res = await executor.executeJob(jobId, job, wf, scoped);
|
|
96
103
|
if (res.ok) {
|
|
97
104
|
scoped = { ...scoped, needs: { ...scoped.needs, [jobId]: { outputs: res.outputs } } };
|
|
@@ -110,8 +117,9 @@ export async function expandJobs(wf, ctx, reader, source, depth = 0, prefix = ""
|
|
|
110
117
|
let status = evalIf(job.if, scoped);
|
|
111
118
|
let reason = job.if != null ? `if: ${JSON.stringify(job.if)}` : "";
|
|
112
119
|
let needs = job.needs ?? [];
|
|
113
|
-
if (typeof needs === "string")
|
|
120
|
+
if (typeof needs === "string") {
|
|
114
121
|
needs = [needs];
|
|
122
|
+
}
|
|
115
123
|
const cond = String(job.if ?? "");
|
|
116
124
|
if (status !== "skipped" && !cond.includes("always()")) {
|
|
117
125
|
for (const n of needs) {
|
package/dist/verify.js
CHANGED
|
@@ -18,8 +18,9 @@ async function actualEntries(octokit, repo, prNumber) {
|
|
|
18
18
|
const entries = new Map();
|
|
19
19
|
const incomplete = [];
|
|
20
20
|
for (const run of runs) {
|
|
21
|
-
if (run.status !== "completed")
|
|
21
|
+
if (run.status !== "completed") {
|
|
22
22
|
incomplete.push(run.path);
|
|
23
|
+
}
|
|
23
24
|
const jobs = await octokit.paginate(octokit.rest.actions.listJobsForWorkflowRun, {
|
|
24
25
|
...base,
|
|
25
26
|
run_id: run.id,
|