willfire 0.1.8 → 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 +20 -5
- 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
|
@@ -535,7 +535,7 @@ export function makeOctokit() {
|
|
|
535
535
|
throw new Error("GH_TOKEN or GITHUB_TOKEN must be set");
|
|
536
536
|
return new Octokit({ auth: token });
|
|
537
537
|
}
|
|
538
|
-
export async function predict(octokit, repo, prNumber) {
|
|
538
|
+
export async function predict(octokit, repo, prNumber, opts = {}) {
|
|
539
539
|
const [owner, name] = repo.split("/");
|
|
540
540
|
const base = { owner, repo: name };
|
|
541
541
|
const { data: pr } = await octokit.rest.pulls.get({ ...base, pull_number: prNumber });
|
|
@@ -545,7 +545,9 @@ export async function predict(octokit, repo, prNumber) {
|
|
|
545
545
|
per_page: 100,
|
|
546
546
|
});
|
|
547
547
|
const ctx = {
|
|
548
|
-
|
|
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"),
|
|
549
551
|
baseRef: pr.base.ref,
|
|
550
552
|
files: files.map((f) => f.filename),
|
|
551
553
|
};
|
|
@@ -662,6 +664,8 @@ function finalizePrediction(entries, skip) {
|
|
|
662
664
|
return { entries: final, checkNames: [...names].sort(), skip };
|
|
663
665
|
}
|
|
664
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";
|
|
665
669
|
function parseArgs(argv) {
|
|
666
670
|
const get = (flag) => {
|
|
667
671
|
const i = argv.indexOf(flag);
|
|
@@ -670,15 +674,26 @@ function parseArgs(argv) {
|
|
|
670
674
|
const repo = get("--repo");
|
|
671
675
|
const pr = get("--pr");
|
|
672
676
|
if (!repo || !pr) {
|
|
673
|
-
console.error(
|
|
677
|
+
console.error(USAGE);
|
|
674
678
|
process.exit(2);
|
|
675
679
|
}
|
|
676
|
-
|
|
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 };
|
|
677
690
|
}
|
|
678
691
|
const isMain = /predict\.(ts|js)$|\/willfire$/.test(process.argv[1] ?? "");
|
|
679
692
|
if (isMain) {
|
|
680
693
|
const args = parseArgs(process.argv.slice(2));
|
|
681
|
-
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
|
+
});
|
|
682
697
|
if (args.json) {
|
|
683
698
|
console.log(JSON.stringify({ entries, checkNames, skip }, null, 2));
|
|
684
699
|
}
|