diff --git a/src/workflows/find-past-built-pr.js b/src/workflows/find-past-built-pr.js index 8c3d3e7dcac1..54bcb1f731e4 100755 --- a/src/workflows/find-past-built-pr.js +++ b/src/workflows/find-past-built-pr.js @@ -5,6 +5,7 @@ import { setOutput } from '@actions/core' import github from './github.js' import { getActionContext } from './action-context.js' +import { octoSecondaryRatelimitRetry } from './secondary-ratelimit-retry.js' async function main() { const sha = await getBuiltSHA() @@ -17,9 +18,10 @@ async function main() { let number = '' const q = `${sha} repo:"${owner}/${repo}"` - const { data } = await octokit.rest.search.issuesAndPullRequests({ q }) + const { data } = await octoSecondaryRatelimitRetry(() => + octokit.rest.search.issuesAndPullRequests({ q }), + ) for (const issue of data.items) { - // console.log(issue) console.log('ID:', issue.id) console.log('Number:', issue.number) console.log('URL:', issue.html_url) diff --git a/src/workflows/secondary-ratelimit-retry.js b/src/workflows/secondary-ratelimit-retry.js new file mode 100644 index 000000000000..24f5826aee7f --- /dev/null +++ b/src/workflows/secondary-ratelimit-retry.js @@ -0,0 +1,44 @@ +import { RequestError } from '@octokit/request-error' + +const DEFAULT_SLEEPTIME = parseInt(process.env.SECONDARY_RATELIMIT_RETRY_SLEEPTIME, 10) || 30_000 +const DEFAULT_ATTEMPTS = parseInt(process.env.SECONDARY_RATELIMIT_RETRY_ATTEMPTS, 10) || 5 + +// Secondary rate limits are responded with a 403. The message will contain +// "You have exceeded a secondary rate limit". +// More info about what they are here: +// https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits +export async function octoSecondaryRatelimitRetry( + fn, + { attempts = DEFAULT_ATTEMPTS, sleepTime = DEFAULT_SLEEPTIME } = {}, +) { + let tries = 0 + while (true) { + try { + return await fn() + } catch (error) { + if ( + error instanceof RequestError && + error.status === 403 && + /You have exceeded a secondary rate limit/.test(error.message) + ) { + if (tries < attempts) { + console.warn( + `Sleeping for ${(sleepTime / 1000).toFixed(1)}s before retrying after ${tries + 1} try`, + ) + await sleep(sleepTime) + tries++ + continue + } else { + console.warn(`Giving up on retries after ${tries + 1} attempts`) + } + } + throw error + } + } +} + +async function sleep(ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms) + }) +}