diff --git a/actions/setup/js/create_pull_request.cjs b/actions/setup/js/create_pull_request.cjs index d90ac8d2d15..07f65f1ca11 100644 --- a/actions/setup/js/create_pull_request.cjs +++ b/actions/setup/js/create_pull_request.cjs @@ -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) @@ -1694,6 +1696,7 @@ ${patchPreview}`; { maxRetries: LABEL_MAX_RETRIES, initialDelayMs: LABEL_INITIAL_DELAY_MS, + maxDelayMs: LABEL_MAX_DELAY_MS, backoffMultiplier: 2, shouldRetry: isLabelTransientError, }, @@ -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)}`); } } diff --git a/actions/setup/js/create_pull_request.test.cjs b/actions/setup/js/create_pull_request.test.cjs index 9c2f4fd7cd0..9de957ecde8 100644 --- a/actions/setup/js/create_pull_request.test.cjs +++ b/actions/setup/js/create_pull_request.test.cjs @@ -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'.")); @@ -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();