Skip to content

Commit

Permalink
fix: retry fetching the URL if the fetch initially fails
Browse files Browse the repository at this point in the history
  • Loading branch information
Codex- committed Jan 29, 2024
1 parent c9a8cb5 commit e08375f
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 15 deletions.
26 changes: 21 additions & 5 deletions dist/index.mjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

91 changes: 89 additions & 2 deletions src/api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

import {
getWorkflowRunActiveJobUrl,
getWorkflowRunActiveJobUrlRetry,
getWorkflowRunFailedJobs,
getWorkflowRunState,
init,
Expand Down Expand Up @@ -222,7 +223,7 @@ describe("API", () => {
);
});

it("should return even if no in_progress job is found", async () => {
it("should return undefined if no in_progress job is found", async () => {
inProgressMockData.jobs[0].status = "unknown";

vi.spyOn(
Expand All @@ -236,7 +237,7 @@ describe("API", () => {
);

const url = await getWorkflowRunActiveJobUrl(123456);
expect(url).toStrictEqual("Unable to fetch URL");
expect(url).toStrictEqual(undefined);
});

it("should return even if GitHub fails to return a URL", async () => {
Expand All @@ -255,6 +256,92 @@ describe("API", () => {
const url = await getWorkflowRunActiveJobUrl(123456);
expect(url).toStrictEqual("GitHub failed to return the URL");
});

describe("getWorkflowRunActiveJobUrlRetry", () => {
beforeEach(() => {
vi.useFakeTimers();
});

afterEach(() => {
vi.useRealTimers();
});

it("should return a message if no job is found", async () => {
inProgressMockData.jobs[0].status = "unknown";

vi.spyOn(
mockOctokit.rest.actions,
"listJobsForWorkflowRun",
).mockReturnValue(
Promise.resolve({
data: inProgressMockData,
status: 200,
}),
);

const urlPromise = getWorkflowRunActiveJobUrlRetry(123456, 100);
vi.advanceTimersByTime(400);
await vi.advanceTimersByTimeAsync(400);

const url = await urlPromise;
expect(url).toStrictEqual("Unable to fetch URL");
});

it("should return a message if no job is found within the timeout period", async () => {
vi.spyOn(mockOctokit.rest.actions, "listJobsForWorkflowRun")
// Final
.mockImplementation(() => {
inProgressMockData.jobs[0].status = "in_progress";

return Promise.resolve({
data: inProgressMockData,
status: 200,
});
})
// First
.mockImplementationOnce(() => {
inProgressMockData.jobs[0].status = "unknown";

return Promise.resolve({
data: inProgressMockData,
status: 200,
});
})
// Second
.mockImplementationOnce(() =>
Promise.resolve({
data: inProgressMockData,
status: 200,
}),
);

const urlPromise = getWorkflowRunActiveJobUrlRetry(123456, 200);
vi.advanceTimersByTime(400);
await vi.advanceTimersByTimeAsync(400);

const url = await urlPromise;
expect(url).toStrictEqual("Unable to fetch URL");
});

it("should return a URL if an in_progress job is found", async () => {
vi.spyOn(
mockOctokit.rest.actions,
"listJobsForWorkflowRun",
).mockImplementation(() =>
Promise.resolve({
data: inProgressMockData,
status: 200,
}),
);

const urlPromise = getWorkflowRunActiveJobUrlRetry(123456, 200);
vi.advanceTimersByTime(400);
await vi.advanceTimersByTimeAsync(400);

const url = await urlPromise;
expect(url).toStrictEqual(inProgressMockData.jobs[0]?.html_url);
});
});
});
});
});
36 changes: 30 additions & 6 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,18 +180,13 @@ export async function getWorkflowRunFailedJobs(

export async function getWorkflowRunActiveJobUrl(
runId: number,
): Promise<string> {
): Promise<string | undefined> {
try {
const response = await getWorkflowRunJobs(runId);
const fetchedInProgressJobs = response.data.jobs.filter(
(job) => job.status === "in_progress",
);

if (fetchedInProgressJobs.length <= 0) {
core.warning(`Failed to find in_progress Jobs for Workflow Run ${runId}`);
return "Unable to fetch URL";
}

core.debug(
`Fetched Jobs for Run:\n` +
` Repository: ${config.owner}/${config.repo}\n` +
Expand All @@ -201,6 +196,10 @@ export async function getWorkflowRunActiveJobUrl(
)}]`,
);

if (fetchedInProgressJobs.length <= 0) {
return undefined;
}

return (
fetchedInProgressJobs[0]?.html_url ?? "GitHub failed to return the URL"
);
Expand All @@ -215,3 +214,28 @@ export async function getWorkflowRunActiveJobUrl(
throw error;
}
}

export async function getWorkflowRunActiveJobUrlRetry(
runId: number,
timeout: number,
): Promise<string> {
const startTime = Date.now();
let elapsedTime = Date.now() - startTime;

while (elapsedTime < timeout) {
elapsedTime = Date.now() - startTime;
core.debug(
`No in_progress Jobs found for Workflow Run ${runId}, retrying...`,
);

const url = await getWorkflowRunActiveJobUrl(runId);
if (url) {
return url;
}

await new Promise((resolve) => setTimeout(resolve, 200));
}
core.debug(`Timed out while trying to fetch URL for Workflow Run ${runId}`);

return "Unable to fetch URL";
}
4 changes: 2 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as core from "@actions/core";
import { getConfig } from "./action.ts";
import {
getWorkflowRunActiveJobUrl,
getWorkflowRunActiveJobUrlRetry,
getWorkflowRunFailedJobs,
getWorkflowRunState,
init,
Expand Down Expand Up @@ -47,7 +47,7 @@ async function run(): Promise<void> {
core.info(
`Awaiting completion of Workflow Run ${config.runId}...\n` +
` ID: ${config.runId}\n` +
` URL: ${await getWorkflowRunActiveJobUrl(config.runId)}`,
` URL: ${await getWorkflowRunActiveJobUrlRetry(config.runId, 1000)}`,
);

while (elapsedTime < timeoutMs) {
Expand Down

0 comments on commit e08375f

Please sign in to comment.