Skip to content

Commit 8f700a1

Browse files
authored
fix: widen release run approval window (#28)
1 parent 360f2bf commit 8f700a1

2 files changed

Lines changed: 35 additions & 3 deletions

File tree

scripts/approve-release-pr-run.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { fileURLToPath } from "node:url";
55

66
const API_VERSION = "2022-11-28";
77
const DEFAULT_ATTEMPTS = 30;
8-
const DEFAULT_INTERVAL_MS = 2_000;
8+
const DEFAULT_INTERVAL_MS = 10_000;
99

1010
function requiredString(value, name) {
1111
if (typeof value !== "string" || value.trim() === "") {

test/release-pr-approval.test.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ function json(value: unknown, status = 200): Response {
2929
});
3030
}
3131

32-
function api(workflowRuns: WorkflowRun[]) {
32+
function api(workflowRuns: WorkflowRun[] | WorkflowRun[][]) {
3333
const requests: Array<{ method: string; url: string }> = [];
34+
const responses = Array.isArray(workflowRuns[0])
35+
? (workflowRuns as WorkflowRun[][])
36+
: [workflowRuns as WorkflowRun[]];
37+
let poll = 0;
3438
const fetchImpl = vi.fn(async (input: string | URL | Request, init?: RequestInit) => {
3539
const url = String(input);
3640
const method = init?.method ?? "GET";
@@ -39,7 +43,9 @@ function api(workflowRuns: WorkflowRun[]) {
3943
return json({ head: { ref: headBranch, sha: headSha, repo: { full_name: repository } } });
4044
}
4145
if (url.includes("/actions/workflows/ci.yml/runs?")) {
42-
return json({ workflow_runs: workflowRuns });
46+
const response = responses[Math.min(poll, responses.length - 1)] ?? [];
47+
poll += 1;
48+
return json({ workflow_runs: response });
4349
}
4450
if (url.endsWith("/actions/runs/42/approve") && method === "POST") {
4551
return new Response(null, { status: 201 });
@@ -80,6 +86,32 @@ describe("release PR held-run approval", () => {
8086
expect(runsRequest?.url).toContain("status=action_required");
8187
});
8288

89+
it("approves a held run that materializes late in the widened polling window", async () => {
90+
const delayedResponses: WorkflowRun[][] = Array.from({ length: 29 }, () => []);
91+
delayedResponses.push([run()]);
92+
const client = api(delayedResponses);
93+
const sleep = vi.fn(async () => {});
94+
95+
const result = await approveReleasePullRequestRun({
96+
releasePr,
97+
repository,
98+
token: "test-token",
99+
apiUrl: "https://api.github.test",
100+
fetchImpl: client.fetchImpl,
101+
sleep,
102+
});
103+
104+
expect(result).toEqual({ runId: 42, headBranch, headSha });
105+
expect(sleep).toHaveBeenCalledTimes(29);
106+
expect(sleep).toHaveBeenCalledWith(10_000);
107+
expect(
108+
client.requests.filter(
109+
({ method, url }) => method === "GET" && url.includes("/actions/workflows/ci.yml/runs?"),
110+
),
111+
).toHaveLength(30);
112+
expect(client.requests.filter(({ method }) => method === "POST")).toHaveLength(1);
113+
});
114+
83115
it("fails the planted dispatch-only negative control", async () => {
84116
const client = api([run({ id: 7, event: "workflow_dispatch" })]);
85117

0 commit comments

Comments
 (0)