Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions actions/setup/js/create_pull_request.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@ function isLabelTransientError(error) {
}

/** @type {number} Number of retry attempts for label operations */
const LABEL_MAX_RETRIES = 3;
/** @type {number} Initial delay in ms before the first label retry (3 seconds) */
const LABEL_MAX_RETRIES = 5;
/** @type {number} Base delay in ms used to calculate label retry backoff (3 seconds) */
const LABEL_INITIAL_DELAY_MS = 3000;
/** @type {number} Maximum delay in ms between label retries (30 seconds) */
const LABEL_MAX_DELAY_MS = 30000;

/**
* Parse allowed base branch patterns from config value (array or comma-separated string)
Expand Down Expand Up @@ -1694,6 +1696,7 @@ ${patchPreview}`;
{
maxRetries: LABEL_MAX_RETRIES,
initialDelayMs: LABEL_INITIAL_DELAY_MS,
maxDelayMs: LABEL_MAX_DELAY_MS,
backoffMultiplier: 2,
shouldRetry: isLabelTransientError,
},
Expand All @@ -1704,6 +1707,8 @@ ${patchPreview}`;
// Label addition is non-critical - warn but don't fail the PR creation.
// GitHub's API may transiently fail to resolve the PR node ID immediately
// after creation, which causes label operations to fail with an unprocessable error.
// If this warning appears, repository checks that require labels on the opened event
// may fail transiently; consider triggering required-label checks on the labeled event instead.
core.warning(`Failed to add labels to PR #${pullRequest.number}: ${labelError instanceof Error ? labelError.message : String(labelError)}`);
}
}
Expand Down
8 changes: 4 additions & 4 deletions actions/setup/js/create_pull_request.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -1181,7 +1181,7 @@ describe("create_pull_request - configured reviewers", () => {

it("should retry addLabels on race condition and warn after all retries exhausted", async () => {
// GitHub API transiently fails to resolve the PR node ID immediately after creation.
// withRetry retries 3 times (4 total calls); after exhaustion it should warn but NOT fall back to an issue.
// withRetry retries 5 times (6 total calls); after exhaustion it should warn but NOT fall back to an issue.
vi.useFakeTimers();
try {
global.github.rest.issues.addLabels.mockRejectedValue(new Error("Validation Failed: Could not resolve to a node with the global id of 'PR_kwDOPc1QR87OOJzM'."));
Expand All @@ -1191,15 +1191,15 @@ describe("create_pull_request - configured reviewers", () => {

const resultPromise = handler({ title: "Test PR", body: "Test body", labels: ["automation"] }, {});

// Advance all fake timers to skip the retry delays (3s, 6s, 12s)
// Advance all fake timers to skip the retry delays (6s, 12s, 24s, 30s, 30s)
await vi.runAllTimersAsync();

const result = await resultPromise;

expect(result.success).toBe(true);
expect(result.fallback_used).toBeUndefined();
// addLabels called once initially + 3 retries = 4 total
expect(global.github.rest.issues.addLabels).toHaveBeenCalledTimes(4);
// addLabels called once initially + 5 retries = 6 total
expect(global.github.rest.issues.addLabels).toHaveBeenCalledTimes(6);
expect(global.core.warning).toHaveBeenCalledWith(expect.stringContaining("Failed to add labels to PR #42"));
} finally {
vi.useRealTimers();
Expand Down
Loading