mirror of
https://github.com/actions/github-script.git
synced 2026-02-08 03:57:27 +00:00
Add the ability to have a delay before retrying
This commit is contained in:
parent
98814c53be
commit
e778eeb4a7
5 changed files with 70 additions and 20 deletions
17
README.md
17
README.md
|
|
@ -104,6 +104,23 @@ By default, requests made with the `github` instance will not be retried. You ca
|
||||||
|
|
||||||
In this example, request failures from `github.rest.issues.get()` will be retried up to 3 times.
|
In this example, request failures from `github.rest.issues.get()` will be retried up to 3 times.
|
||||||
|
|
||||||
|
You can also configure the delay (delayInSeconds) before retrying via the `retry-after` option:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: actions/github-script@v6
|
||||||
|
id: my-script
|
||||||
|
with:
|
||||||
|
result-encoding: string
|
||||||
|
retries: 3
|
||||||
|
retry-after: 500
|
||||||
|
script: |
|
||||||
|
github.rest.issues.get({
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
You can also configure which status codes should be exempt from retries via the `retry-exempt-status-codes` option:
|
You can also configure which status codes should be exempt from retries via the `retry-exempt-status-codes` option:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
import {getRetryOptions} from '../src/retry-options'
|
import { getRetryOptions } from '../src/retry-options'
|
||||||
|
|
||||||
describe('getRequestOptions', () => {
|
describe('getRequestOptions', () => {
|
||||||
test('retries disabled if retries == 0', async () => {
|
test('retries disabled if retries == 0', async () => {
|
||||||
const [retryOptions, requestOptions] = getRetryOptions(
|
const [retryOptions, requestOptions] = getRetryOptions(
|
||||||
0,
|
0,
|
||||||
[400, 500, 502],
|
[400, 500, 502],
|
||||||
|
321,
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(retryOptions.enabled).toBe(false)
|
expect(retryOptions.enabled).toBe(false)
|
||||||
expect(retryOptions.doNotRetry).toBeFalsy()
|
expect(retryOptions.doNotRetry).toBeFalsy()
|
||||||
|
expect(retryOptions.delay).toBeFalsy()
|
||||||
|
|
||||||
expect(requestOptions?.retries).toBeFalsy()
|
expect(requestOptions?.retries).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
@ -20,39 +22,59 @@ describe('getRequestOptions', () => {
|
||||||
const [retryOptions, requestOptions] = getRetryOptions(
|
const [retryOptions, requestOptions] = getRetryOptions(
|
||||||
1,
|
1,
|
||||||
[400, 500, 502],
|
[400, 500, 502],
|
||||||
|
321,
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(retryOptions.enabled).toBe(true)
|
expect(retryOptions.enabled).toBe(true)
|
||||||
expect(retryOptions.doNotRetry).toEqual([400, 500, 502])
|
expect(retryOptions.doNotRetry).toEqual([400, 500, 502])
|
||||||
|
expect(retryOptions.delay).toEqual(321)
|
||||||
|
|
||||||
expect(requestOptions?.retries).toEqual(1)
|
expect(requestOptions?.retries).toEqual(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('properties set if retries > 0', async () => {
|
test('delay should be undefined if retryAfter < 0', async () => {
|
||||||
const [retryOptions, requestOptions] = getRetryOptions(
|
const [retryOptions, requestOptions] = getRetryOptions(
|
||||||
1,
|
1,
|
||||||
[400, 500, 502],
|
[400, 500, 502],
|
||||||
|
-1,
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(retryOptions.enabled).toBe(true)
|
expect(retryOptions.enabled).toBe(true)
|
||||||
expect(retryOptions.doNotRetry).toEqual([400, 500, 502])
|
expect(retryOptions.doNotRetry).toEqual([400, 500, 502])
|
||||||
|
expect(retryOptions.delay).toBeUndefined()
|
||||||
|
|
||||||
|
expect(requestOptions?.retries).toEqual(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('delay should be undefined if retryAfter = 0', async () => {
|
||||||
|
const [retryOptions, requestOptions] = getRetryOptions(
|
||||||
|
1,
|
||||||
|
[400, 500, 502],
|
||||||
|
0,
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(retryOptions.enabled).toBe(true)
|
||||||
|
expect(retryOptions.doNotRetry).toEqual([400, 500, 502])
|
||||||
|
expect(retryOptions.delay).toBeUndefined()
|
||||||
|
|
||||||
expect(requestOptions?.retries).toEqual(1)
|
expect(requestOptions?.retries).toEqual(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('retryOptions.doNotRetry not set if exemptStatusCodes isEmpty', async () => {
|
test('retryOptions.doNotRetry not set if exemptStatusCodes isEmpty', async () => {
|
||||||
const [retryOptions, requestOptions] = getRetryOptions(1, [], [])
|
const [retryOptions, requestOptions] = getRetryOptions(1, [], 321, [])
|
||||||
|
|
||||||
expect(retryOptions.enabled).toBe(true)
|
expect(retryOptions.enabled).toBe(true)
|
||||||
expect(retryOptions.doNotRetry).toBeUndefined()
|
expect(retryOptions.doNotRetry).toBeUndefined()
|
||||||
|
expect(retryOptions.delay).toBe(321)
|
||||||
|
|
||||||
expect(requestOptions?.retries).toEqual(1)
|
expect(requestOptions?.retries).toEqual(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('requestOptions does not override defaults from @actions/github', async () => {
|
test('requestOptions does not override defaults from @actions/github', async () => {
|
||||||
const [retryOptions, requestOptions] = getRetryOptions(1, [], {
|
const [retryOptions, requestOptions] = getRetryOptions(1, [], 321, {
|
||||||
request: {
|
request: {
|
||||||
agent: 'default-user-agent'
|
agent: 'default-user-agent'
|
||||||
},
|
},
|
||||||
|
|
@ -60,6 +82,7 @@ describe('getRequestOptions', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(retryOptions.enabled).toBe(true)
|
expect(retryOptions.enabled).toBe(true)
|
||||||
|
expect(retryOptions.delay).toBe(321)
|
||||||
expect(retryOptions.doNotRetry).toBeUndefined()
|
expect(retryOptions.doNotRetry).toBeUndefined()
|
||||||
|
|
||||||
expect(requestOptions?.retries).toEqual(1)
|
expect(requestOptions?.retries).toEqual(1)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,9 @@ inputs:
|
||||||
retries:
|
retries:
|
||||||
description: The number of times to retry a request
|
description: The number of times to retry a request
|
||||||
default: "0"
|
default: "0"
|
||||||
|
retry-after:
|
||||||
|
description: The number of seconds after which the retry attempt should be made. No effect unless `retries` is set
|
||||||
|
default: "1000"
|
||||||
retry-exempt-status-codes:
|
retry-exempt-status-codes:
|
||||||
description: A comma separated list of status codes that will NOT be retried e.g. "400,500". No effect unless `retries` is set
|
description: A comma separated list of status codes that will NOT be retried e.g. "400,500". No effect unless `retries` is set
|
||||||
default: 400,401,403,404,422 # from https://github.com/octokit/plugin-retry.js/blob/9a2443746c350b3beedec35cf26e197ea318a261/src/index.ts#L14
|
default: 400,401,403,404,422 # from https://github.com/octokit/plugin-retry.js/blob/9a2443746c350b3beedec35cf26e197ea318a261/src/index.ts#L14
|
||||||
|
|
|
||||||
20
src/main.ts
20
src/main.ts
|
|
@ -1,15 +1,15 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
import {context, getOctokit} from '@actions/github'
|
import { context, getOctokit } from '@actions/github'
|
||||||
import {defaults as defaultGitHubOptions} from '@actions/github/lib/utils'
|
import { defaults as defaultGitHubOptions } from '@actions/github/lib/utils'
|
||||||
import * as glob from '@actions/glob'
|
import * as glob from '@actions/glob'
|
||||||
import * as io from '@actions/io'
|
import * as io from '@actions/io'
|
||||||
import {retry} from '@octokit/plugin-retry'
|
import { retry } from '@octokit/plugin-retry'
|
||||||
import {RequestRequestOptions} from '@octokit/types'
|
import { RequestRequestOptions } from '@octokit/types'
|
||||||
import {callAsyncFunction} from './async-function'
|
|
||||||
import {getRetryOptions, parseNumberArray, RetryOptions} from './retry-options'
|
|
||||||
import {wrapRequire} from './wrap-require'
|
|
||||||
import fetch from 'node-fetch'
|
import fetch from 'node-fetch'
|
||||||
|
import { callAsyncFunction } from './async-function'
|
||||||
|
import { getRetryOptions, parseNumberArray, RetryOptions } from './retry-options'
|
||||||
|
import { wrapRequire } from './wrap-require'
|
||||||
|
|
||||||
process.on('unhandledRejection', handleError)
|
process.on('unhandledRejection', handleError)
|
||||||
main().catch(handleError)
|
main().catch(handleError)
|
||||||
|
|
@ -23,17 +23,19 @@ type Options = {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
async function main(): Promise<void> {
|
||||||
const token = core.getInput('github-token', {required: true})
|
const token = core.getInput('github-token', { required: true })
|
||||||
const debug = core.getInput('debug')
|
const debug = core.getInput('debug')
|
||||||
const userAgent = core.getInput('user-agent')
|
const userAgent = core.getInput('user-agent')
|
||||||
const previews = core.getInput('previews')
|
const previews = core.getInput('previews')
|
||||||
const retries = parseInt(core.getInput('retries'))
|
const retries = parseInt(core.getInput('retries'))
|
||||||
|
const retryAfter = parseInt(core.getInput('retry-after'))
|
||||||
const exemptStatusCodes = parseNumberArray(
|
const exemptStatusCodes = parseNumberArray(
|
||||||
core.getInput('retry-exempt-status-codes')
|
core.getInput('retry-exempt-status-codes')
|
||||||
)
|
)
|
||||||
const [retryOpts, requestOpts] = getRetryOptions(
|
const [retryOpts, requestOpts] = getRetryOptions(
|
||||||
retries,
|
retries,
|
||||||
exemptStatusCodes,
|
exemptStatusCodes,
|
||||||
|
retryAfter,
|
||||||
defaultGitHubOptions
|
defaultGitHubOptions
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -45,7 +47,7 @@ async function main(): Promise<void> {
|
||||||
if (requestOpts) opts.request = requestOpts
|
if (requestOpts) opts.request = requestOpts
|
||||||
|
|
||||||
const github = getOctokit(token, opts, retry)
|
const github = getOctokit(token, opts, retry)
|
||||||
const script = core.getInput('script', {required: true})
|
const script = core.getInput('script', { required: true })
|
||||||
|
|
||||||
// Using property/value shorthand on `require` (e.g. `{require}`) causes compilation errors.
|
// Using property/value shorthand on `require` (e.g. `{require}`) causes compilation errors.
|
||||||
const result = await callAsyncFunction(
|
const result = await callAsyncFunction(
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,31 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {OctokitOptions} from '@octokit/core/dist-types/types'
|
import { OctokitOptions } from '@octokit/core/dist-types/types'
|
||||||
import {RequestRequestOptions} from '@octokit/types'
|
import { RequestRequestOptions } from '@octokit/types'
|
||||||
|
|
||||||
export type RetryOptions = {
|
export type RetryOptions = {
|
||||||
doNotRetry?: number[]
|
doNotRetry?: number[]
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
|
delay?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRetryOptions(
|
export function getRetryOptions(
|
||||||
retries: number,
|
retries: number,
|
||||||
exemptStatusCodes: number[],
|
exemptStatusCodes: number[],
|
||||||
|
retryAfter: number,
|
||||||
defaultOptions: OctokitOptions
|
defaultOptions: OctokitOptions
|
||||||
): [RetryOptions, RequestRequestOptions | undefined] {
|
): [RetryOptions, RequestRequestOptions | undefined] {
|
||||||
if (retries <= 0) {
|
if (retries <= 0) {
|
||||||
return [{enabled: false}, defaultOptions.request]
|
return [{ enabled: false }, defaultOptions.request]
|
||||||
}
|
}
|
||||||
|
|
||||||
const retryOptions: RetryOptions = {
|
const retryOptions: RetryOptions = {
|
||||||
enabled: true
|
enabled: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (retryAfter > 0) {
|
||||||
|
retryOptions.delay = retryAfter
|
||||||
|
}
|
||||||
|
|
||||||
if (exemptStatusCodes.length > 0) {
|
if (exemptStatusCodes.length > 0) {
|
||||||
retryOptions.doNotRetry = exemptStatusCodes
|
retryOptions.doNotRetry = exemptStatusCodes
|
||||||
}
|
}
|
||||||
|
|
@ -33,10 +39,9 @@ export function getRetryOptions(
|
||||||
}
|
}
|
||||||
|
|
||||||
core.debug(
|
core.debug(
|
||||||
`GitHub client configured with: (retries: ${
|
`GitHub client configured with: (retries: ${requestOptions.retries
|
||||||
requestOptions.retries
|
}, retryAfter: ${retryOptions.delay ?? 'octokit default: 1000'
|
||||||
}, retry-exempt-status-code: ${
|
} retry-exempt-status-code: ${retryOptions?.doNotRetry ?? 'octokit default: [400, 401, 403, 404, 422]'
|
||||||
retryOptions?.doNotRetry ?? 'octokit default: [400, 401, 403, 404, 422]'
|
|
||||||
})`
|
})`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue