Skip to content

Commit 68f2833

Browse files
committed
make WORKFLOW_JOB_STEPS_RETRY_MS as an action input parameter and linear backoff strategy for retries
1 parent e484f0b commit 68f2833

File tree

11 files changed

+65
-21
lines changed

11 files changed

+65
-21
lines changed

.github/workflows/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ jobs:
2626
workflow: dispatch.yml
2727
workflow_inputs: '{"cake":"delicious"}'
2828
workflow_timeout_seconds: 30
29+
workflow_job_steps_retry_seconds: 10
2930
- name: Evaluate that the Run ID output has been set
3031
run: |
3132
if [ "${{ steps.return_dispatch.outputs.run_id }}" == "" ]; then

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ steps:
2525
workflow: automation-test.yml
2626
workflow_inputs: '{ "some_input": "value" }' # Optional
2727
workflow_timeout_seconds: 120 # Default: 300
28+
workflow_job_steps_retry_seconds: 10 # Default: 5
2829
distinct_id: someDistinctId # Optional
2930

3031
- name: Use the output run ID and URL

action.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ inputs:
3030
workflow_timeout_seconds:
3131
description: Time until giving up waiting for the start of the workflow run.
3232
default: 300
33+
workflow_job_steps_retry_seconds:
34+
description: Duration to wait between retries while monitoring for the workflow run to start.
35+
default: 5
3336
distinct_id:
3437
description: Specify a static string to use instead of a random distinct ID.
3538

src/action.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ describe("Action", () => {
2727
workflow: "workflow_name",
2828
workflow_inputs: JSON.stringify(workflowInputs),
2929
workflow_timeout_seconds: "60",
30+
workflow_job_steps_retry_seconds: "3",
3031
distinct_id: "distinct_id",
3132
};
3233

@@ -47,6 +48,8 @@ describe("Action", () => {
4748
return mockEnvConfig.workflow_inputs;
4849
case "workflow_timeout_seconds":
4950
return mockEnvConfig.workflow_timeout_seconds;
51+
case "workflow_job_steps_retry_seconds":
52+
return mockEnvConfig.workflow_job_steps_retry_seconds;
5053
case "distinct_id":
5154
return mockEnvConfig.distinct_id;
5255
default:
@@ -71,6 +74,7 @@ describe("Action", () => {
7174
expect(config.workflow).toStrictEqual("workflow_name");
7275
expect(config.workflowInputs).toStrictEqual(workflowInputs);
7376
expect(config.workflowTimeoutSeconds).toStrictEqual(60);
77+
expect(config.workflowJobStepsRetrySeconds).toStrictEqual(3);
7478
expect(config.distinctId).toStrictEqual("distinct_id");
7579
});
7680

@@ -88,6 +92,13 @@ describe("Action", () => {
8892
expect(config.workflowTimeoutSeconds).toStrictEqual(300);
8993
});
9094

95+
it("should provide a default workflow job step retry if none is supplied", () => {
96+
mockEnvConfig.workflow_job_steps_retry_seconds = "";
97+
const config: ActionConfig = getConfig();
98+
99+
expect(config.workflowJobStepsRetrySeconds).toStrictEqual(5);
100+
});
101+
91102
it("should handle no inputs being provided", () => {
92103
mockEnvConfig.workflow_inputs = "";
93104
const config: ActionConfig = getConfig();

src/action.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as core from "@actions/core";
22
import { v4 as uuid } from "uuid";
33

44
const WORKFLOW_TIMEOUT_SECONDS = 5 * 60;
5+
const WORKFLOW_JOB_STEPS_RETRY_SECONDS = 5
56

67
/**
78
* action.yaml definition.
@@ -42,6 +43,11 @@ export interface ActionConfig {
4243
*/
4344
workflowTimeoutSeconds: number;
4445

46+
/**
47+
* Time in retries for identifying the Run ID.
48+
*/
49+
workflowJobStepsRetrySeconds: number;
50+
4551
/**
4652
* Specify a static ID to use instead of a distinct ID.
4753
*/
@@ -68,6 +74,9 @@ export function getConfig(): ActionConfig {
6874
workflowTimeoutSeconds:
6975
getNumberFromValue(core.getInput("workflow_timeout_seconds")) ??
7076
WORKFLOW_TIMEOUT_SECONDS,
77+
workflowJobStepsRetrySeconds:
78+
getNumberFromValue(core.getInput("workflow_job_steps_retry_seconds")) ??
79+
WORKFLOW_JOB_STEPS_RETRY_SECONDS,
7180
distinctId:
7281
getOptionalWorkflowValue(core.getInput("distinct_id")) ?? uuid(),
7382
};

src/api.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ describe("API", () => {
9595
return JSON.stringify({ testInput: "test" });
9696
case "workflow_timeout_seconds":
9797
return "30";
98+
case "workflow_job_steps_retry_seconds":
99+
return "5";
98100
default:
99101
return "";
100102
}
@@ -332,6 +334,7 @@ describe("API", () => {
332334
workflow: "workflow_name",
333335
workflowInputs: { testInput: "test" },
334336
workflowTimeoutSeconds: 60,
337+
workflowJobStepsRetrySeconds: 3,
335338
distinctId: "test-uuid",
336339
};
337340

src/constants.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/* eslint-disable @typescript-eslint/no-inferrable-types */
22

33
export const WORKFLOW_FETCH_TIMEOUT_MS: number = 60 * 1000;
4-
export const WORKFLOW_JOB_STEPS_RETRY_MS: number = 5000;
54
export const WORKFLOW_JOB_STEPS_SERVER_ERROR_RETRY_MAX: number = 3;
65
export const WORKFLOW_JOB_STEPS_SERVER_ERROR_RETRY_MS: number = 500;

src/main.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ describe("main", () => {
3535
ref: "test-ref",
3636
workflow: "test-workflow",
3737
workflowTimeoutSeconds: 0,
38+
workflowJobStepsRetrySeconds: 0,
3839
} satisfies Partial<action.ActionConfig> as action.ActionConfig;
3940
const testBranch: utils.BranchNameResult = {
4041
branchName: "test-branch",
@@ -173,6 +174,7 @@ describe("main", () => {
173174
distinctIdRegex: distinctIdRegex,
174175
workflowId: 0,
175176
workflowTimeoutMs: testCfg.workflowTimeoutSeconds * 1000,
177+
workflowJobStepsRetryMs :testCfg.workflowJobStepsRetrySeconds * 1000,
176178
});
177179

178180
// Result

src/main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export async function main(): Promise<void> {
4444
distinctIdRegex,
4545
workflowId,
4646
workflowTimeoutMs: config.workflowTimeoutSeconds * 1000,
47+
workflowJobStepsRetryMs: config.workflowJobStepsRetrySeconds * 1000,
4748
});
4849
if (result.success) {
4950
handleActionSuccess(result.value.id, result.value.url);

src/return-dispatch.spec.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ describe("return-dispatch", () => {
479479
distinctIdRegex: distinctIdRegex,
480480
workflowId: workflowId,
481481
workflowTimeoutMs: 100,
482+
workflowJobStepsRetryMs: 5,
482483
};
483484

484485
let apiFetchWorkflowRunIdsMock: MockInstance<
@@ -661,13 +662,14 @@ describe("return-dispatch", () => {
661662
.mockResolvedValueOnce({ success: true, value: [] });
662663
apiFetchWorkflowRunJobStepsMock.mockResolvedValue([distinctId]);
663664
apiFetchWorkflowRunUrlMock.mockResolvedValue(runUrl);
664-
vi.spyOn(constants, "WORKFLOW_JOB_STEPS_RETRY_MS", "get").mockReturnValue(
665-
5000,
666-
);
667665

666+
const retryMs = 5000;
667+
const timeoutMs = 60 * 60 * 100;
668+
668669
const getRunIdAndUrlPromise = getRunIdAndUrl({
669670
...defaultOpts,
670-
workflowTimeoutMs: 60 * 60 * 1000,
671+
workflowTimeoutMs: timeoutMs,
672+
workflowJobStepsRetryMs: retryMs,
671673
});
672674

673675
// First attempt
@@ -680,10 +682,10 @@ describe("return-dispatch", () => {
680682
expect(coreInfoLogMock.mock.calls[0]?.[0]).toMatchSnapshot();
681683

682684
expect(utilSleepMock).toHaveBeenCalledOnce();
683-
expect(utilSleepMock).toHaveBeenCalledWith(5000);
685+
expect(utilSleepMock).toHaveBeenCalledWith(retryMs);
684686

685687
resetLogMocks();
686-
await vi.advanceTimersByTimeAsync(5000);
688+
await vi.advanceTimersByTimeAsync(retryMs);
687689

688690
// Second attempt
689691
expect(apiRetryOrTimeoutMock).toHaveBeenCalledTimes(2);
@@ -694,10 +696,10 @@ describe("return-dispatch", () => {
694696
expect(coreInfoLogMock.mock.calls[0]?.[0]).toMatchSnapshot();
695697

696698
expect(utilSleepMock).toHaveBeenCalledTimes(2);
697-
expect(utilSleepMock).toHaveBeenCalledWith(5000);
699+
expect(utilSleepMock).toHaveBeenCalledWith(retryMs * 2);
698700

699701
resetLogMocks();
700-
await vi.advanceTimersByTimeAsync(5000);
702+
await vi.advanceTimersByTimeAsync(retryMs * 2);
701703

702704
// Third attempt
703705
expect(apiRetryOrTimeoutMock).toHaveBeenCalledTimes(3);
@@ -770,13 +772,14 @@ describe("return-dispatch", () => {
770772
});
771773
apiFetchWorkflowRunJobStepsMock.mockResolvedValue([]);
772774
apiFetchWorkflowRunUrlMock.mockResolvedValue(runUrl);
773-
vi.spyOn(constants, "WORKFLOW_JOB_STEPS_RETRY_MS", "get").mockReturnValue(
774-
5000,
775-
);
775+
776+
const retryMs = 3000;
777+
const timeoutMs = 15 * 1000;
776778

777779
const getRunIdAndUrlPromise = getRunIdAndUrl({
778780
...defaultOpts,
779-
workflowTimeoutMs: 15 * 1000,
781+
workflowTimeoutMs: timeoutMs,
782+
workflowJobStepsRetryMs: retryMs,
780783
});
781784

782785
// First attempt
@@ -792,10 +795,10 @@ describe("return-dispatch", () => {
792795
expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchSnapshot();
793796

794797
expect(utilSleepMock).toHaveBeenCalledOnce();
795-
expect(utilSleepMock).toHaveBeenCalledWith(5000);
798+
expect(utilSleepMock).toHaveBeenCalledWith(retryMs);
796799

797800
resetLogMocks();
798-
await vi.advanceTimersByTimeAsync(5000);
801+
await vi.advanceTimersByTimeAsync(retryMs);
799802

800803
// Second attempt
801804
expect(apiRetryOrTimeoutMock).toHaveBeenCalledTimes(2);
@@ -809,10 +812,10 @@ describe("return-dispatch", () => {
809812
expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchSnapshot();
810813

811814
expect(utilSleepMock).toHaveBeenCalledTimes(2);
812-
expect(utilSleepMock).toHaveBeenCalledWith(5000);
815+
expect(utilSleepMock).toHaveBeenCalledWith(retryMs * 2);
813816

814817
resetLogMocks();
815-
await vi.advanceTimersByTimeAsync(5000);
818+
await vi.advanceTimersByTimeAsync(retryMs * 2);
816819

817820
// Timeout attempt
818821
expect(apiRetryOrTimeoutMock).toHaveBeenCalledTimes(3);
@@ -826,10 +829,10 @@ describe("return-dispatch", () => {
826829
expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchSnapshot();
827830

828831
expect(utilSleepMock).toHaveBeenCalledTimes(3);
829-
expect(utilSleepMock).toHaveBeenCalledWith(5000);
832+
expect(utilSleepMock).toHaveBeenCalledWith(retryMs * 3);
830833

831834
resetLogMocks();
832-
await vi.advanceTimersByTimeAsync(5000);
835+
await vi.advanceTimersByTimeAsync(retryMs * 3);
833836

834837
// Result
835838
const run = await getRunIdAndUrlPromise;

0 commit comments

Comments
 (0)