willfire 0.1.15 → 0.1.16
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/matrix/expandMatrixDetailed.js +20 -10
- package/dist/matrix/formatMatrixValue.js +6 -3
- package/dist/matrix/matrixSuffix.js +2 -1
- package/dist/predict/finalizePrediction.js +2 -1
- package/dist/predict/makeOctokit.js +2 -1
- package/dist/predict/predict.js +32 -43
- package/dist/predict/stackTargetRef.js +8 -4
- package/package.json +1 -1
|
@@ -9,35 +9,43 @@ import { evaluateValue } from "../expr/evaluateValue.js";
|
|
|
9
9
|
* the whole job `unknown` rather than a guess at how many checks it creates.
|
|
10
10
|
*/
|
|
11
11
|
function axisValues(v, scope) {
|
|
12
|
-
if (Array.isArray(v))
|
|
12
|
+
if (Array.isArray(v)) {
|
|
13
13
|
return v;
|
|
14
|
-
|
|
14
|
+
}
|
|
15
|
+
if (typeof v !== "string") {
|
|
15
16
|
return null;
|
|
17
|
+
}
|
|
16
18
|
const val = evaluateValue(v, scope);
|
|
17
|
-
if (val.kind !== "json" || !Array.isArray(val.v))
|
|
19
|
+
if (val.kind !== "json" || !Array.isArray(val.v)) {
|
|
18
20
|
return null;
|
|
21
|
+
}
|
|
19
22
|
return val.v;
|
|
20
23
|
}
|
|
21
24
|
export function expandMatrixDetailed(strategy, scope = {}) {
|
|
22
25
|
const matrix = strategy?.matrix;
|
|
23
|
-
if (matrix == null)
|
|
26
|
+
if (matrix == null) {
|
|
24
27
|
return [null];
|
|
28
|
+
}
|
|
25
29
|
// `matrix: ${{ ... }}` — the whole matrix as one expression, rather than the
|
|
26
30
|
// per-axis form below. It yields include-style entries, not axes, so it is a
|
|
27
31
|
// separate expansion and is not modelled.
|
|
28
|
-
if (typeof matrix === "string")
|
|
32
|
+
if (typeof matrix === "string") {
|
|
29
33
|
return null;
|
|
34
|
+
}
|
|
30
35
|
const include = matrix.include ?? [];
|
|
31
36
|
const exclude = matrix.exclude ?? [];
|
|
32
|
-
if (typeof include === "string" || typeof exclude === "string")
|
|
37
|
+
if (typeof include === "string" || typeof exclude === "string") {
|
|
33
38
|
return null;
|
|
39
|
+
}
|
|
34
40
|
const axes = {};
|
|
35
41
|
for (const [k, v] of Object.entries(matrix)) {
|
|
36
|
-
if (k === "include" || k === "exclude")
|
|
42
|
+
if (k === "include" || k === "exclude") {
|
|
37
43
|
continue;
|
|
44
|
+
}
|
|
38
45
|
const vals = axisValues(v, scope);
|
|
39
|
-
if (vals == null)
|
|
46
|
+
if (vals == null) {
|
|
40
47
|
return null;
|
|
48
|
+
}
|
|
41
49
|
axes[k] = vals;
|
|
42
50
|
}
|
|
43
51
|
const axisKeys = Object.keys(axes);
|
|
@@ -45,8 +53,9 @@ export function expandMatrixDetailed(strategy, scope = {}) {
|
|
|
45
53
|
for (const [k, vals] of Object.entries(axes)) {
|
|
46
54
|
combos = combos.flatMap((c) => vals.map((v) => ({ values: { ...c.values, [k]: v }, displayKeys: axisKeys })));
|
|
47
55
|
}
|
|
48
|
-
if (axisKeys.length === 0)
|
|
56
|
+
if (axisKeys.length === 0) {
|
|
49
57
|
combos = [];
|
|
58
|
+
}
|
|
50
59
|
combos = combos.filter((c) => !exclude.some((ex) => Object.entries(ex).every(([k, v]) => c.values[k] === v)));
|
|
51
60
|
const extra = [];
|
|
52
61
|
for (const inc of include) {
|
|
@@ -57,8 +66,9 @@ export function expandMatrixDetailed(strategy, scope = {}) {
|
|
|
57
66
|
// matches every combination, per the docs ("added to each of the matrix
|
|
58
67
|
// combinations if none of the key:value pairs overwrite any of the
|
|
59
68
|
// original matrix values").
|
|
60
|
-
for (const c of targets)
|
|
69
|
+
for (const c of targets) {
|
|
61
70
|
Object.assign(c.values, inc);
|
|
71
|
+
}
|
|
62
72
|
}
|
|
63
73
|
else {
|
|
64
74
|
// No combination to attach to: the include entry becomes a combination
|
|
@@ -6,11 +6,14 @@
|
|
|
6
6
|
* `m-object (linux, x64)`.
|
|
7
7
|
*/
|
|
8
8
|
export function formatMatrixValue(v) {
|
|
9
|
-
if (v == null)
|
|
9
|
+
if (v == null) {
|
|
10
10
|
return "";
|
|
11
|
-
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(v)) {
|
|
12
13
|
return v.map(formatMatrixValue).join(", ");
|
|
13
|
-
|
|
14
|
+
}
|
|
15
|
+
if (typeof v === "object") {
|
|
14
16
|
return Object.values(v).map(formatMatrixValue).join(", ");
|
|
17
|
+
}
|
|
15
18
|
return String(v);
|
|
16
19
|
}
|
|
@@ -2,7 +2,8 @@ import { formatMatrixValue } from "./formatMatrixValue.js";
|
|
|
2
2
|
/** The ` (v1, v2)` suffix GitHub appends for a matrix combination. */
|
|
3
3
|
export function matrixSuffix(combo) {
|
|
4
4
|
const keys = combo.displayKeys.filter((k) => k in combo.values);
|
|
5
|
-
if (keys.length === 0)
|
|
5
|
+
if (keys.length === 0) {
|
|
6
6
|
return "";
|
|
7
|
+
}
|
|
7
8
|
return ` (${keys.map((k) => formatMatrixValue(combo.values[k])).join(", ")})`;
|
|
8
9
|
}
|
|
@@ -7,8 +7,9 @@ export function finalizePrediction(entries, skip, sources) {
|
|
|
7
7
|
const final = entries.map(finalize);
|
|
8
8
|
const names = new Set();
|
|
9
9
|
for (const e of final) {
|
|
10
|
-
if (e.status === "run" && e.checkName
|
|
10
|
+
if (e.status === "run" && e.checkName !== null) {
|
|
11
11
|
names.add(e.checkName);
|
|
12
|
+
}
|
|
12
13
|
}
|
|
13
14
|
return {
|
|
14
15
|
entries: final,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Octokit } from "@octokit/rest";
|
|
2
2
|
export function makeOctokit() {
|
|
3
3
|
const token = process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN;
|
|
4
|
-
if (!token)
|
|
4
|
+
if (!token) {
|
|
5
5
|
throw new Error("GH_TOKEN or GITHUB_TOKEN must be set");
|
|
6
|
+
}
|
|
6
7
|
return new Octokit({ auth: token });
|
|
7
8
|
}
|
package/dist/predict/predict.js
CHANGED
|
@@ -30,7 +30,7 @@ export async function predict(octokit, repo, prNumber, opts = {}) {
|
|
|
30
30
|
// is a guess kept only so existing callers keep working.
|
|
31
31
|
action: opts.action ?? (pr.commits > 1 ? "synchronize" : "opened"),
|
|
32
32
|
baseRef: pr.base.ref,
|
|
33
|
-
...(stackTarget
|
|
33
|
+
...(stackTarget !== null ? { stackTarget } : {}),
|
|
34
34
|
files: files.map((f) => f.filename),
|
|
35
35
|
};
|
|
36
36
|
const headSha = pr.head.sha;
|
|
@@ -58,8 +58,9 @@ export async function predict(octokit, repo, prNumber, opts = {}) {
|
|
|
58
58
|
const resolveRef = async (src) => {
|
|
59
59
|
const key = sourceKey(src);
|
|
60
60
|
const hit = refCache.get(key);
|
|
61
|
-
if (hit !== undefined)
|
|
61
|
+
if (hit !== undefined) {
|
|
62
62
|
return hit;
|
|
63
|
+
}
|
|
63
64
|
let sha;
|
|
64
65
|
try {
|
|
65
66
|
const { data } = await octokit.rest.repos.getCommit({
|
|
@@ -75,8 +76,9 @@ export async function predict(octokit, repo, prNumber, opts = {}) {
|
|
|
75
76
|
sha = null;
|
|
76
77
|
}
|
|
77
78
|
refCache.set(key, sha);
|
|
78
|
-
if (sha
|
|
79
|
+
if (sha !== null) {
|
|
79
80
|
sources.set(key, { ...src, sha });
|
|
81
|
+
}
|
|
80
82
|
return sha;
|
|
81
83
|
};
|
|
82
84
|
// One callee is commonly reached from several callers — a fleet repo calls
|
|
@@ -89,8 +91,9 @@ export async function predict(octokit, repo, prNumber, opts = {}) {
|
|
|
89
91
|
// that moves mid-prediction cannot hand back two different files.
|
|
90
92
|
const key = `${src.owner}/${src.repo}/${path}@${src.sha}`;
|
|
91
93
|
const hit = cache.get(key);
|
|
92
|
-
if (hit !== undefined)
|
|
94
|
+
if (hit !== undefined) {
|
|
93
95
|
return hit;
|
|
96
|
+
}
|
|
94
97
|
let content;
|
|
95
98
|
try {
|
|
96
99
|
const { data } = await octokit.rest.repos.getContent({
|
|
@@ -115,7 +118,7 @@ export async function predict(octokit, repo, prNumber, opts = {}) {
|
|
|
115
118
|
// from the tarball endpoint at the resolved commit, and every subprocess —
|
|
116
119
|
// `tar` included — goes through the one `runShell` seam.
|
|
117
120
|
let executor;
|
|
118
|
-
if (opts.execute
|
|
121
|
+
if (opts.execute !== undefined && opts.execute.length > 0) {
|
|
119
122
|
const download = async (src) => {
|
|
120
123
|
try {
|
|
121
124
|
const { data } = await octokit.rest.repos.downloadTarballArchive({
|
|
@@ -152,32 +155,20 @@ export async function predict(octokit, repo, prNumber, opts = {}) {
|
|
|
152
155
|
const prFacts = {
|
|
153
156
|
github: { repository: `${headSource.owner}/${headSource.repo}` },
|
|
154
157
|
};
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (w.state !== "active") {
|
|
161
|
-
entries.push({
|
|
162
|
-
workflow: path,
|
|
163
|
-
job: "*",
|
|
164
|
-
status: "no-dispatch",
|
|
165
|
-
reason: `workflow state: ${w.state}`,
|
|
166
|
-
});
|
|
167
|
-
continue;
|
|
158
|
+
const workflowEntries = async (path, state) => {
|
|
159
|
+
if (state !== "active") {
|
|
160
|
+
return [
|
|
161
|
+
{ workflow: path, job: "*", status: "no-dispatch", reason: `workflow state: ${state}` },
|
|
162
|
+
];
|
|
168
163
|
}
|
|
169
164
|
const content = await fetchWorkflow(path, headSource);
|
|
170
|
-
if (content
|
|
165
|
+
if (content === null) {
|
|
171
166
|
// The Actions API keeps listing a workflow as `active` after its file is
|
|
172
167
|
// deleted. There is no file at head, so there is nothing to dispatch —
|
|
173
168
|
// the same verdict as the disabled case above, reached a different way.
|
|
174
|
-
|
|
175
|
-
workflow: path,
|
|
176
|
-
|
|
177
|
-
status: "no-dispatch",
|
|
178
|
-
reason: "no workflow file at head",
|
|
179
|
-
});
|
|
180
|
-
continue;
|
|
169
|
+
return [
|
|
170
|
+
{ workflow: path, job: "*", status: "no-dispatch", reason: "no workflow file at head" },
|
|
171
|
+
];
|
|
181
172
|
}
|
|
182
173
|
let wf;
|
|
183
174
|
try {
|
|
@@ -187,27 +178,25 @@ export async function predict(octokit, repo, prNumber, opts = {}) {
|
|
|
187
178
|
// GitHub creates a run for an unparseable workflow file and concludes it
|
|
188
179
|
// `startup_failure`. The run exists but has no jobs, so this is a
|
|
189
180
|
// workflow-level "it dispatches" with nothing to expand.
|
|
190
|
-
|
|
191
|
-
workflow: path,
|
|
192
|
-
job: "*",
|
|
193
|
-
status: "run",
|
|
194
|
-
reason: `YAML parse error: ${e}`,
|
|
195
|
-
});
|
|
196
|
-
continue;
|
|
181
|
+
return [{ workflow: path, job: "*", status: "run", reason: `YAML parse error: ${e}` }];
|
|
197
182
|
}
|
|
198
183
|
const [dispatches, reason] = workflowDispatches(wf, ctx);
|
|
199
184
|
if (!dispatches) {
|
|
200
|
-
|
|
201
|
-
continue;
|
|
185
|
+
return [{ workflow: path, job: "*", status: "no-dispatch", reason }];
|
|
202
186
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
187
|
+
const jobs = await expandJobs(wf, ctx, reader, headSource, 0, "", true, prFacts, executor);
|
|
188
|
+
return jobs.map((j) => ({
|
|
189
|
+
workflow: path,
|
|
190
|
+
job: jobName(j.job),
|
|
191
|
+
checkName: j.checkName,
|
|
192
|
+
status: j.status,
|
|
193
|
+
reason: j.reason || reason,
|
|
194
|
+
}));
|
|
195
|
+
};
|
|
196
|
+
const entries = [];
|
|
197
|
+
for (const w of workflows) {
|
|
198
|
+
if (w.path.startsWith(".github/workflows/")) {
|
|
199
|
+
entries.push(...(await workflowEntries(w.path, w.state)));
|
|
211
200
|
}
|
|
212
201
|
}
|
|
213
202
|
return finalizePrediction(entries, null, sources);
|
|
@@ -17,24 +17,27 @@ export async function stackTargetRef(octokit, owner, repo, pr) {
|
|
|
17
17
|
try {
|
|
18
18
|
for (let hop = 0; hop < MAX_STACK_DEPTH; hop++) {
|
|
19
19
|
const mergeSha = cur.merge_commit_sha;
|
|
20
|
-
if (mergeSha
|
|
20
|
+
if (mergeSha === null) {
|
|
21
21
|
break;
|
|
22
|
+
}
|
|
22
23
|
const { data: preview } = await octokit.rest.repos.getCommit({
|
|
23
24
|
owner,
|
|
24
25
|
repo,
|
|
25
26
|
ref: mergeSha,
|
|
26
27
|
});
|
|
27
28
|
const previewParent = preview.parents[0]?.sha;
|
|
28
|
-
if (previewParent
|
|
29
|
+
if (previewParent === undefined) {
|
|
29
30
|
break;
|
|
31
|
+
}
|
|
30
32
|
const { data: baseTip } = await octokit.rest.repos.getCommit({
|
|
31
33
|
owner,
|
|
32
34
|
repo,
|
|
33
35
|
ref: cur.base.ref,
|
|
34
36
|
});
|
|
35
37
|
// Built on the base branch tip: normal mode, the walk is done.
|
|
36
|
-
if (previewParent === baseTip.sha)
|
|
38
|
+
if (previewParent === baseTip.sha) {
|
|
37
39
|
break;
|
|
40
|
+
}
|
|
38
41
|
// Otherwise only an exact match against an open PR whose head is the
|
|
39
42
|
// base branch proves stacked mode; a stale preview matches nothing.
|
|
40
43
|
const { data: candidates } = await octokit.rest.pulls.list({
|
|
@@ -45,8 +48,9 @@ export async function stackTargetRef(octokit, owner, repo, pr) {
|
|
|
45
48
|
per_page: 100,
|
|
46
49
|
});
|
|
47
50
|
const parent = candidates.find((p) => p.merge_commit_sha === previewParent);
|
|
48
|
-
if (parent
|
|
51
|
+
if (parent === undefined) {
|
|
49
52
|
break;
|
|
53
|
+
}
|
|
50
54
|
target = parent.base.ref;
|
|
51
55
|
cur = parent;
|
|
52
56
|
}
|