willfire 0.1.29 → 0.1.30
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/jobs/calleeInputs.d.ts +12 -0
- package/dist/jobs/calleeInputs.js +28 -0
- package/dist/jobs/expandJobs.d.ts +2 -2
- package/dist/jobs/expandJobs.js +2 -82
- package/dist/jobs/inputValue.d.ts +7 -0
- package/dist/jobs/inputValue.js +30 -0
- package/dist/jobs/neededJobIds.d.ts +7 -0
- package/dist/jobs/neededJobIds.js +15 -0
- package/dist/jobs/workflowCallInputs.d.ts +3 -0
- package/dist/jobs/workflowCallInputs.js +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Scope, type Val } from "../expr/val.js";
|
|
2
|
+
import type { Workflow } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* What `inputs.*` resolves to inside a called workflow: what the caller passed,
|
|
5
|
+
* over the defaults the callee declares.
|
|
6
|
+
*
|
|
7
|
+
* A declared input the caller omits falls back to its `default`. A declared
|
|
8
|
+
* input with no default and no caller value is unknown rather than empty — the
|
|
9
|
+
* workflow would be invalid if it were required, and guessing empty would
|
|
10
|
+
* silently decide guards that are not decided.
|
|
11
|
+
*/
|
|
12
|
+
export declare function calleeInputs(withBlock: unknown, subWf: Workflow, scope: Scope): Record<string, Val>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { UNKNOWN } from "../expr/val.js";
|
|
2
|
+
import { inputValue } from "./inputValue.js";
|
|
3
|
+
import { workflowCallInputs } from "./workflowCallInputs.js";
|
|
4
|
+
/**
|
|
5
|
+
* What `inputs.*` resolves to inside a called workflow: what the caller passed,
|
|
6
|
+
* over the defaults the callee declares.
|
|
7
|
+
*
|
|
8
|
+
* A declared input the caller omits falls back to its `default`. A declared
|
|
9
|
+
* input with no default and no caller value is unknown rather than empty — the
|
|
10
|
+
* workflow would be invalid if it were required, and guessing empty would
|
|
11
|
+
* silently decide guards that are not decided.
|
|
12
|
+
*/
|
|
13
|
+
export function calleeInputs(withBlock, subWf, scope) {
|
|
14
|
+
const out = {};
|
|
15
|
+
for (const [name, decl] of Object.entries(workflowCallInputs(subWf))) {
|
|
16
|
+
out[name] =
|
|
17
|
+
decl !== null && typeof decl === "object" && "default" in decl
|
|
18
|
+
? // Defaults live in the callee, out of the caller's context's reach.
|
|
19
|
+
inputValue(decl["default"], {})
|
|
20
|
+
: UNKNOWN;
|
|
21
|
+
}
|
|
22
|
+
if (withBlock !== null && typeof withBlock === "object") {
|
|
23
|
+
for (const [name, raw] of Object.entries(withBlock)) {
|
|
24
|
+
out[name] = inputValue(raw, scope);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { Scope } from "../expr/val.js";
|
|
2
|
+
import type { JobExecutor } from "../execute.js";
|
|
3
3
|
import type { Ctx, ExpandedJob, Workflow, WorkflowReader, WorkflowSource } from "../types.js";
|
|
4
4
|
export declare function expandJobs(wf: Workflow, ctx: Ctx, reader: WorkflowReader, source: WorkflowSource, depth?: number, prefix?: string, prefixResolved?: boolean, scope?: Scope, executor?: JobExecutor): Promise<ExpandedJob[]>;
|
package/dist/jobs/expandJobs.js
CHANGED
|
@@ -1,77 +1,12 @@
|
|
|
1
1
|
import { parse as parseYaml } from "yaml";
|
|
2
|
-
import { evaluateValue } from "../expr/evaluateValue.js";
|
|
3
|
-
import { UNKNOWN } from "../expr/val.js";
|
|
4
|
-
import { renderTemplate } from "../execute.js";
|
|
5
2
|
import { expandMatrixDetailed } from "../matrix/expandMatrixDetailed.js";
|
|
6
3
|
import { jobDisplayName } from "../names/jobDisplayName.js";
|
|
7
4
|
import { skippedDisplayName } from "../names/skippedDisplayName.js";
|
|
8
5
|
import { parseUses } from "../uses/parseUses.js";
|
|
6
|
+
import { calleeInputs } from "./calleeInputs.js";
|
|
9
7
|
import { evalIf } from "./evalIf.js";
|
|
8
|
+
import { neededJobIds } from "./neededJobIds.js";
|
|
10
9
|
import { prScope } from "./prScope.js";
|
|
11
|
-
/**
|
|
12
|
-
* A `with:` value as the callee will see it, evaluated in the caller's scope.
|
|
13
|
-
* A whole-expression value keeps its evaluated type; mixed text renders to a
|
|
14
|
-
* string, all or nothing; anything unresolvable stays unknown.
|
|
15
|
-
*/
|
|
16
|
-
function inputValue(raw, scope) {
|
|
17
|
-
if (raw === null || raw === undefined) {
|
|
18
|
-
return { kind: "value", v: "" };
|
|
19
|
-
}
|
|
20
|
-
if (typeof raw === "boolean" || typeof raw === "number") {
|
|
21
|
-
return { kind: "value", v: raw };
|
|
22
|
-
}
|
|
23
|
-
if (typeof raw !== "string") {
|
|
24
|
-
return UNKNOWN;
|
|
25
|
-
}
|
|
26
|
-
if (!raw.includes("${{")) {
|
|
27
|
-
return { kind: "value", v: raw };
|
|
28
|
-
}
|
|
29
|
-
const t = raw.trim();
|
|
30
|
-
// `${{a}} x ${{b}}` fails this test and takes the render path instead.
|
|
31
|
-
if (t.startsWith("${{") && t.indexOf("}}") === t.length - 2) {
|
|
32
|
-
return evaluateValue(t.slice(3, -2), prScope(scope));
|
|
33
|
-
}
|
|
34
|
-
const rendered = renderTemplate(raw, prScope(scope));
|
|
35
|
-
return rendered === null ? UNKNOWN : { kind: "value", v: rendered };
|
|
36
|
-
}
|
|
37
|
-
/** The `on.workflow_call.inputs` block, tolerating the YAML 1.1 `on` -> true key. */
|
|
38
|
-
function workflowCallInputs(wf) {
|
|
39
|
-
const on = wf?.["on"] ?? wf?.["true"];
|
|
40
|
-
if (on === null || typeof on !== "object") {
|
|
41
|
-
return {};
|
|
42
|
-
}
|
|
43
|
-
const call = on["workflow_call"];
|
|
44
|
-
if (call === null || call === undefined) {
|
|
45
|
-
return {};
|
|
46
|
-
}
|
|
47
|
-
const inputs = call["inputs"];
|
|
48
|
-
return inputs !== null && typeof inputs === "object" ? inputs : {};
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* What `inputs.*` resolves to inside a called workflow: what the caller passed,
|
|
52
|
-
* over the defaults the callee declares.
|
|
53
|
-
*
|
|
54
|
-
* A declared input the caller omits falls back to its `default`. A declared
|
|
55
|
-
* input with no default and no caller value is unknown rather than empty — the
|
|
56
|
-
* workflow would be invalid if it were required, and guessing empty would
|
|
57
|
-
* silently decide guards that are not decided.
|
|
58
|
-
*/
|
|
59
|
-
function calleeInputs(withBlock, subWf, scope) {
|
|
60
|
-
const out = {};
|
|
61
|
-
for (const [name, decl] of Object.entries(workflowCallInputs(subWf))) {
|
|
62
|
-
out[name] =
|
|
63
|
-
decl !== null && typeof decl === "object" && "default" in decl
|
|
64
|
-
? // Defaults live in the callee, out of the caller's context's reach.
|
|
65
|
-
inputValue(decl["default"], {})
|
|
66
|
-
: UNKNOWN;
|
|
67
|
-
}
|
|
68
|
-
if (withBlock !== null && typeof withBlock === "object") {
|
|
69
|
-
for (const [name, raw] of Object.entries(withBlock)) {
|
|
70
|
-
out[name] = inputValue(raw, scope);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return out;
|
|
74
|
-
}
|
|
75
10
|
/**
|
|
76
11
|
* GitHub allows a reusable-workflow call chain four levels deep. Past that the
|
|
77
12
|
* run itself fails, so anything deeper is not a name we could predict anyway.
|
|
@@ -84,21 +19,6 @@ const MAX_REUSABLE_DEPTH = 4;
|
|
|
84
19
|
/** `ref` is already a commit id, so resolving it is a no-op. */
|
|
85
20
|
const SHA_RE = /^[0-9a-f]{40}$/i;
|
|
86
21
|
const isSha = (ref) => SHA_RE.test(ref);
|
|
87
|
-
const NEEDS_OUTPUTS_RE = /needs\s*\.\s*([A-Za-z_][A-Za-z0-9_-]*)\s*\.\s*outputs\b/g;
|
|
88
|
-
/**
|
|
89
|
-
* The jobs some sibling reads outputs from — the only jobs worth executing.
|
|
90
|
-
* Matching over the serialized job catches every read site without modelling
|
|
91
|
-
* any; a false positive costs one wasted run, never a verdict.
|
|
92
|
-
*/
|
|
93
|
-
function neededJobIds(jobs) {
|
|
94
|
-
const needed = new Set();
|
|
95
|
-
for (const job of Object.values(jobs)) {
|
|
96
|
-
for (const m of JSON.stringify(job ?? {}).matchAll(NEEDS_OUTPUTS_RE)) {
|
|
97
|
-
needed.add(m[1]);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return needed;
|
|
101
|
-
}
|
|
102
22
|
export async function expandJobs(wf, ctx, reader, source, depth = 0, prefix = "", prefixResolved = true, scope = {}, executor) {
|
|
103
23
|
const entries = [];
|
|
104
24
|
const jobs = wf.jobs ?? {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Scope, type Val } from "../expr/val.js";
|
|
2
|
+
/**
|
|
3
|
+
* A `with:` value as the callee will see it, evaluated in the caller's scope.
|
|
4
|
+
* A whole-expression value keeps its evaluated type; mixed text renders to a
|
|
5
|
+
* string, all or nothing; anything unresolvable stays unknown.
|
|
6
|
+
*/
|
|
7
|
+
export declare function inputValue(raw: unknown, scope: Scope): Val;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { evaluateValue } from "../expr/evaluateValue.js";
|
|
2
|
+
import { UNKNOWN } from "../expr/val.js";
|
|
3
|
+
import { renderTemplate } from "../execute.js";
|
|
4
|
+
import { prScope } from "./prScope.js";
|
|
5
|
+
/**
|
|
6
|
+
* A `with:` value as the callee will see it, evaluated in the caller's scope.
|
|
7
|
+
* A whole-expression value keeps its evaluated type; mixed text renders to a
|
|
8
|
+
* string, all or nothing; anything unresolvable stays unknown.
|
|
9
|
+
*/
|
|
10
|
+
export function inputValue(raw, scope) {
|
|
11
|
+
if (raw === null || raw === undefined) {
|
|
12
|
+
return { kind: "value", v: "" };
|
|
13
|
+
}
|
|
14
|
+
if (typeof raw === "boolean" || typeof raw === "number") {
|
|
15
|
+
return { kind: "value", v: raw };
|
|
16
|
+
}
|
|
17
|
+
if (typeof raw !== "string") {
|
|
18
|
+
return UNKNOWN;
|
|
19
|
+
}
|
|
20
|
+
if (!raw.includes("${{")) {
|
|
21
|
+
return { kind: "value", v: raw };
|
|
22
|
+
}
|
|
23
|
+
const t = raw.trim();
|
|
24
|
+
// `${{a}} x ${{b}}` fails this test and takes the render path instead.
|
|
25
|
+
if (t.startsWith("${{") && t.indexOf("}}") === t.length - 2) {
|
|
26
|
+
return evaluateValue(t.slice(3, -2), prScope(scope));
|
|
27
|
+
}
|
|
28
|
+
const rendered = renderTemplate(raw, prScope(scope));
|
|
29
|
+
return rendered === null ? UNKNOWN : { kind: "value", v: rendered };
|
|
30
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Workflow } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The jobs some sibling reads outputs from — the only jobs worth executing.
|
|
4
|
+
* Matching over the serialized job catches every read site without modelling
|
|
5
|
+
* any; a false positive costs one wasted run, never a verdict.
|
|
6
|
+
*/
|
|
7
|
+
export declare function neededJobIds(jobs: Record<string, Workflow>): Set<string>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const NEEDS_OUTPUTS_RE = /needs\s*\.\s*([A-Za-z_][A-Za-z0-9_-]*)\s*\.\s*outputs\b/g;
|
|
2
|
+
/**
|
|
3
|
+
* The jobs some sibling reads outputs from — the only jobs worth executing.
|
|
4
|
+
* Matching over the serialized job catches every read site without modelling
|
|
5
|
+
* any; a false positive costs one wasted run, never a verdict.
|
|
6
|
+
*/
|
|
7
|
+
export function neededJobIds(jobs) {
|
|
8
|
+
const needed = new Set();
|
|
9
|
+
for (const job of Object.values(jobs)) {
|
|
10
|
+
for (const m of JSON.stringify(job ?? {}).matchAll(NEEDS_OUTPUTS_RE)) {
|
|
11
|
+
needed.add(m[1]);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return needed;
|
|
15
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The `on.workflow_call.inputs` block, tolerating the YAML 1.1 `on` -> true key. */
|
|
2
|
+
export function workflowCallInputs(wf) {
|
|
3
|
+
const on = wf?.["on"] ?? wf?.["true"];
|
|
4
|
+
const inputs = on?.["workflow_call"]?.["inputs"];
|
|
5
|
+
return inputs !== null && typeof inputs === "object" ? inputs : {};
|
|
6
|
+
}
|