willfire 0.1.7 → 0.1.9
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/README.md +16 -3
- package/dist/predict.d.ts +31 -2
- package/dist/predict.js +24 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,10 +20,19 @@ pnpm add willfire
|
|
|
20
20
|
import { predict } from "willfire";
|
|
21
21
|
import { getOctokit } from "@actions/github"; // or new Octokit({ auth: token })
|
|
22
22
|
|
|
23
|
-
const { entries, checkNames, skip } = await predict(getOctokit(token), "owner/repo", 123
|
|
23
|
+
const { entries, checkNames, skip } = await predict(getOctokit(token), "owner/repo", 123, {
|
|
24
|
+
action: context.payload.action, // "opened" | "synchronize" | "reopened"
|
|
25
|
+
});
|
|
24
26
|
// checkNames: sorted, deduped checkName of every entry with status "run"
|
|
25
27
|
```
|
|
26
28
|
|
|
29
|
+
`action` is optional but worth passing. Omitted, the event action is inferred
|
|
30
|
+
from the PR's commit count, which is wrong in both directions — a PR opened
|
|
31
|
+
from a branch with several commits looks like `synchronize`, a force-push down
|
|
32
|
+
to one commit looks like `opened` — and can never produce `reopened`. That only
|
|
33
|
+
matters to a workflow narrowing `types:`, where it decides whether the workflow
|
|
34
|
+
dispatches at all.
|
|
35
|
+
|
|
27
36
|
`entries` is a union of two variants, both carrying `workflow` and `reason`:
|
|
28
37
|
|
|
29
38
|
| variant | `job` | `checkName` | `status` |
|
|
@@ -58,7 +67,8 @@ Auth is any token with `contents: read`, `actions: read`, and
|
|
|
58
67
|
## CLI
|
|
59
68
|
|
|
60
69
|
```sh
|
|
61
|
-
GH_TOKEN=... willfire --repo owner/repo --pr 123
|
|
70
|
+
GH_TOKEN=... willfire --repo owner/repo --pr 123 \
|
|
71
|
+
[--action opened|synchronize|reopened] [--json]
|
|
62
72
|
```
|
|
63
73
|
|
|
64
74
|
## What it handles
|
|
@@ -124,7 +134,10 @@ the docs do not state:
|
|
|
124
134
|
|
|
125
135
|
Scope notes: validated on `opened` pull_request events; `synchronize`/`labeled`
|
|
126
136
|
live events, `branches-ignore`, and diffs far beyond 301 files are not yet
|
|
127
|
-
probe-verified
|
|
137
|
+
probe-verified — passing `action` explicitly is what makes probing the other
|
|
138
|
+
two possible. `action` accepts the three default `types:` only; `pull_request`
|
|
139
|
+
fires on around twenty actions, and a workflow narrowing to `ready_for_review`
|
|
140
|
+
or `edited` is out of scope either way. Reusable-workflow name prefixing is probe-verified to three
|
|
128
141
|
levels; deeper nesting is inferred. The cross-repo probe calls back into the
|
|
129
142
|
probe repo itself by full `owner/repo@ref` reference, so it pins ref
|
|
130
143
|
resolution but not the owner/repo half of the address.
|
package/dist/predict.d.ts
CHANGED
|
@@ -41,6 +41,11 @@ export interface WorkflowEntry extends EntryBase {
|
|
|
41
41
|
* These can be genuinely undecidable statically — dynamic matrix, a reusable
|
|
42
42
|
* workflow we cannot read, unresolvable `if`, or `needs` on any of those — so
|
|
43
43
|
* `"unknown"` lives here and only here.
|
|
44
|
+
*
|
|
45
|
+
* `"no-dispatch"` is the mirror image, and lives on {@link WorkflowEntry} and
|
|
46
|
+
* only there. It is a verdict about whether the run happens at all, which is
|
|
47
|
+
* settled before any job is looked at — so by the time there is a job entry to
|
|
48
|
+
* label, the answer is already yes.
|
|
44
49
|
*/
|
|
45
50
|
export interface JobEntry extends EntryBase {
|
|
46
51
|
job: JobName;
|
|
@@ -54,7 +59,7 @@ export interface JobEntry extends EntryBase {
|
|
|
54
59
|
* interpolates something we cannot evaluate ahead of the run.
|
|
55
60
|
*/
|
|
56
61
|
checkName: string | null;
|
|
57
|
-
status: "run" | "skipped" | "unknown"
|
|
62
|
+
status: "run" | "skipped" | "unknown";
|
|
58
63
|
}
|
|
59
64
|
export type Entry = WorkflowEntry | JobEntry;
|
|
60
65
|
/** Narrow to the workflow-level variant without inspecting the sentinel. */
|
|
@@ -74,6 +79,30 @@ export interface Prediction {
|
|
|
74
79
|
export declare function patternToRegex(pat: string): RegExp;
|
|
75
80
|
/** Order-sensitive match: last matching pattern wins; ! negates. */
|
|
76
81
|
export declare function matchFilters(value: string, patterns: string[]): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* The `pull_request` actions a caller can name.
|
|
84
|
+
*
|
|
85
|
+
* Deliberately the three default types and no more. `pull_request` fires on
|
|
86
|
+
* around twenty actions — `ready_for_review`, `edited`, `labeled` — and a
|
|
87
|
+
* workflow that narrows `types:` to one of those is not predictable today
|
|
88
|
+
* regardless of what is passed here. Widening this union later is a
|
|
89
|
+
* non-breaking change; narrowing it would not be, so it starts narrow.
|
|
90
|
+
*/
|
|
91
|
+
export type PrEventAction = "opened" | "synchronize" | "reopened";
|
|
92
|
+
/** Options every caller may omit. */
|
|
93
|
+
export interface PredictOptions {
|
|
94
|
+
/**
|
|
95
|
+
* The literal event action the run was triggered by — `github.event.action`
|
|
96
|
+
* inside an Action, or `--action` on the CLI.
|
|
97
|
+
*
|
|
98
|
+
* Omitting it falls back to inferring from the commit count, which is a
|
|
99
|
+
* guess and is wrong in both directions: a PR opened from a branch with
|
|
100
|
+
* several commits looks like `synchronize`, a force-push down to one commit
|
|
101
|
+
* looks like `opened`, and `reopened` is never produced. That only matters
|
|
102
|
+
* to a workflow narrowing `types:`, where it matters completely.
|
|
103
|
+
*/
|
|
104
|
+
action?: PrEventAction;
|
|
105
|
+
}
|
|
77
106
|
export interface Ctx {
|
|
78
107
|
action: string;
|
|
79
108
|
baseRef: string;
|
|
@@ -148,5 +177,5 @@ export declare function parseUses(uses: string): UsesTarget | null;
|
|
|
148
177
|
*/
|
|
149
178
|
export declare function expandWorkflowJobs(wf: Workflow, ctx: Ctx, fetchWorkflow: FetchWorkflow, source: WorkflowSource): Promise<ExpandedJob[]>;
|
|
150
179
|
export declare function makeOctokit(): Octokit;
|
|
151
|
-
export declare function predict(octokit: Octokit, repo: string, prNumber: number): Promise<Prediction>;
|
|
180
|
+
export declare function predict(octokit: Octokit, repo: string, prNumber: number, opts?: PredictOptions): Promise<Prediction>;
|
|
152
181
|
export {};
|
package/dist/predict.js
CHANGED
|
@@ -171,7 +171,10 @@ function expandMatrixDetailed(strategy) {
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
combos.push(...extra);
|
|
174
|
-
|
|
174
|
+
// Zero combinations is a real answer, not a missing one: an empty axis, or an
|
|
175
|
+
// `exclude` that removes everything, schedules no jobs at all. Only an absent
|
|
176
|
+
// `matrix:` key means "one unsuffixed job", and that returned above.
|
|
177
|
+
return combos;
|
|
175
178
|
}
|
|
176
179
|
/** Return list of matrix combination dicts, or null if dynamic. */
|
|
177
180
|
export function expandMatrix(strategy) {
|
|
@@ -532,7 +535,7 @@ export function makeOctokit() {
|
|
|
532
535
|
throw new Error("GH_TOKEN or GITHUB_TOKEN must be set");
|
|
533
536
|
return new Octokit({ auth: token });
|
|
534
537
|
}
|
|
535
|
-
export async function predict(octokit, repo, prNumber) {
|
|
538
|
+
export async function predict(octokit, repo, prNumber, opts = {}) {
|
|
536
539
|
const [owner, name] = repo.split("/");
|
|
537
540
|
const base = { owner, repo: name };
|
|
538
541
|
const { data: pr } = await octokit.rest.pulls.get({ ...base, pull_number: prNumber });
|
|
@@ -542,7 +545,9 @@ export async function predict(octokit, repo, prNumber) {
|
|
|
542
545
|
per_page: 100,
|
|
543
546
|
});
|
|
544
547
|
const ctx = {
|
|
545
|
-
|
|
548
|
+
// The caller's answer wins whenever it has one. The commit-count fallback
|
|
549
|
+
// is a guess kept only so existing callers keep working.
|
|
550
|
+
action: opts.action ?? (pr.commits > 1 ? "synchronize" : "opened"),
|
|
546
551
|
baseRef: pr.base.ref,
|
|
547
552
|
files: files.map((f) => f.filename),
|
|
548
553
|
};
|
|
@@ -659,6 +664,8 @@ function finalizePrediction(entries, skip) {
|
|
|
659
664
|
return { entries: final, checkNames: [...names].sort(), skip };
|
|
660
665
|
}
|
|
661
666
|
// ------------------------------------------------------------------------ CLI
|
|
667
|
+
const USAGE = "usage: predict --repo owner/name --pr N [--action opened|synchronize|reopened] [--json]";
|
|
668
|
+
const isPrEventAction = (v) => v === "opened" || v === "synchronize" || v === "reopened";
|
|
662
669
|
function parseArgs(argv) {
|
|
663
670
|
const get = (flag) => {
|
|
664
671
|
const i = argv.indexOf(flag);
|
|
@@ -667,15 +674,26 @@ function parseArgs(argv) {
|
|
|
667
674
|
const repo = get("--repo");
|
|
668
675
|
const pr = get("--pr");
|
|
669
676
|
if (!repo || !pr) {
|
|
670
|
-
console.error(
|
|
677
|
+
console.error(USAGE);
|
|
671
678
|
process.exit(2);
|
|
672
679
|
}
|
|
673
|
-
|
|
680
|
+
// An unrecognised action is refused rather than ignored. Silently falling
|
|
681
|
+
// back to the guess would turn a typo into a wrong prediction, which is the
|
|
682
|
+
// failure this flag exists to remove.
|
|
683
|
+
const action = get("--action");
|
|
684
|
+
if (action !== undefined && !isPrEventAction(action)) {
|
|
685
|
+
console.error(`unknown --action: ${action}`);
|
|
686
|
+
console.error(USAGE);
|
|
687
|
+
process.exit(2);
|
|
688
|
+
}
|
|
689
|
+
return { repo, pr: Number(pr), json: argv.includes("--json"), action };
|
|
674
690
|
}
|
|
675
691
|
const isMain = /predict\.(ts|js)$|\/willfire$/.test(process.argv[1] ?? "");
|
|
676
692
|
if (isMain) {
|
|
677
693
|
const args = parseArgs(process.argv.slice(2));
|
|
678
|
-
const { entries, checkNames, skip } = await predict(makeOctokit(), args.repo, args.pr
|
|
694
|
+
const { entries, checkNames, skip } = await predict(makeOctokit(), args.repo, args.pr, {
|
|
695
|
+
action: args.action,
|
|
696
|
+
});
|
|
679
697
|
if (args.json) {
|
|
680
698
|
console.log(JSON.stringify({ entries, checkNames, skip }, null, 2));
|
|
681
699
|
}
|